Reproduction
With Bun 1.4.1, MySQL 8.4.5 and published bun-query-builder 0.2.69, QueryEvent.sql is the literal string [object Promise] for ordinary INSERT, SELECT and UPDATE builders. The queries execute successfully.
Register hooks.onQueryStart, then execute these against a disposable MySQL table:
await db.insertInto('hook_probe').values({ id: 1, value: 10 }).execute()
await db.selectFrom('hook_probe').where('id', '=', 1).selectAll().execute()
await db.updateTable('hook_probe').set({ value: 11 }).where('id', '=', 1).execute()Observed events:
insert: [object Promise]
select: [object Promise]
update: [object Promise]Expected: parameterized SQL text describing each executed statement. Checking only typeof event.sql === 'string' misses this regression because the placeholder is itself a string.
Owning layer and impact
Reproduced directly through the installed package without Stacks imports. runWithHooks() obtains its text from computeSqlText(q), which uses String(q). The native Bun MySQL query does not expose its SQL through this conversion. The SQLite wrapper supplies its own toString(), so existing SQLite hook tests pass.
This prevents log filtering, query diagnostics and tracing consumers from identifying executed SQL. It was discovered while adding a deterministic concurrent session-revocation check to Stacks; a hook filtering by event.sql.includes('sessions') never matched.
Related coverage work: https://github.com/stacksjs/stacks/issues/2607, shipped in https://github.com/stacksjs/stacks/commit/792c0e496ea1fc99e25f7ac847eaf3d9376da1c8. That commit tests the session behavior; it does not fix this upstream diagnostic contract.
The fix belongs in query metadata capture/rendering in this package. It must retain parameterized text rather than interpolate values or reconstruct SQL from an opaque Promise. No query execution failure or session-revocation bypass was observed in this investigation. Tagged/subquery, transaction, error/slow-hook and PostgreSQL paths need coverage when fixing this.