Found on main @ 68fd61c, verified by execution.
A hasMany/hasOne/belongsTo relation resolves the related table by pluralising the model name instead of reading the related model's declared table. Any model whose table is not exactly toTableName(name) cannot be eager-loaded.
Repro
const Widget = createModel({
name: 'Widget',
table: 'wg_custom_widgets', // <- declared, and not 'widgets'
primaryKey: 'id',
autoIncrement: true,
attributes: { owner_id: { type: 'integer', fillable: true }, label: { type: 'string', fillable: true } },
} as const)
const Owner = createModel({
name: 'Owner',
table: 'wg_owners',
primaryKey: 'id',
autoIncrement: true,
attributes: { name: { type: 'string', fillable: true } },
hasMany: ['Widget'],
} as const)
await Owner.query().with('widget').get()SQLiteError: no such table: widgetsBoth models were created with createModel and both tables exist. wg_custom_widgets is never consulted.
Source
resolveRelation in packages/bun-query-builder/src/orm.ts (~1621, and the same line repeated for hasOne / belongsTo / belongsToMany):
const relatedTable = relatedModel?.getTable?.() || toTableName(hasManyModel)The intent is right — prefer the declared table, fall back to the name. But relatedModel?.getTable?.() is coming back undefined here despite Widget having been created, so the fallback is what actually runs. The registry lookup that produces relatedModel is the thing to fix; the fallback then only applies to genuinely unregistered models, which is what it reads as being for.
Impact
Silent for anyone whose tables happen to match the default pluralisation, and a hard failure for anyone using a table prefix, a legacy schema, or an irregular plural — i.e. it works until you point it at an existing database.