Repository navigation
Description
Report hasn't been filed before.
- I have verified that the bug I'm about to report hasn't been filed before.
What version of drizzle-orm are you using?
1.0.0-rc.5
What version of drizzle-kit are you using?
1.0.0-rc.5
Other packages
No response
Describe the Bug
With the bun-sql driver (default options, i.e. prepared statements), inserting a top-level number or boolean into a jsonb column through the query builder fails:
column "value" is of type jsonb but expression is of type integer
column "value" is of type jsonb but expression is of type boolean
Strings, plain objects and arrays work.
Environment: Bun 1.4.4, PostgreSQL 17.10
Minimal reproduction:
import { drizzle } from 'drizzle-orm/bun-sql';
import { jsonb, pgTable, text } from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
const db = drizzle(process.env.DATABASE_URL!);
const settings = pgTable('settings', { key: text().primaryKey(), value: jsonb() });
await db.execute(sql`create temp table settings (key text primary key, value jsonb)`);
const values: Array<[string, unknown]> = [
['number', 42],
['boolean', false],
['string', 'hello'],
['object', { a: 1 }],
['array', [1, 'a']],
];
for (const [key, value] of values) {
try {
await db.insert(settings).values({ key, value });
console.log(key.padEnd(8), 'ok');
} catch (error: any) {
console.log(key.padEnd(8), 'FAILED:', error.cause?.message ?? error.message);
}
}Output:
number FAILED: column "value" is of type jsonb but expression is of type integer
boolean FAILED: column "value" is of type jsonb but expression is of type boolean
string ok
object ok
array ok
Expected: all values are stored as jsonb (42, false, "hello", {"a": 1}, [1, "a"]).
Cause
bunSqlPgCodecs defines only normalizeParamArray for json/jsonb, so the raw JS value is passed to Bun as is. Plain Bun.SQL behaves the same way: it binds JS numbers and booleans with their own Postgres types, and its docs say to use sql.json(value) to store a JSON number or boolean:
await sql`insert into t values (${42})`; // FAIL: expression is of type integer
await sql`insert into t values (${sql.json(42)})`; // ok, stored as jsonb numberSo this is documented Bun behavior, but drizzle knows the column is jsonb, so I think the codec should handle it. Users currently can't store top-level numbers/booleans in jsonb with bun-sql at all, unless they override codecs.
Note that a plain normalizeParam: (v) => JSON.stringify(v) (as in #6139 / the workaround in #6132) is not a correct fix for the default prepared mode: Bun already JSON-encodes strings and objects there, so stringified strings end up double-encoded ("\"hello\""). Wrapping the value with sql.json() (or only serializing numbers/booleans) seems like the right direction.
Related: #6582 (same missing scalar normalizeParam, postgres-js driver), #6132 (bun-sql json input with prepare: false). From what I can see, the bun-sql codec changes in #6588 (rc.6) only touch the output side.
Activity
Gourav224 commentedon Oct 25, 2026
I have a fix on my fork. Opening a PR is currently restricted on this repo, so I'm linking it here.
The
json/jsonbcodecs indrizzle-orm/src/bun-sql/postgres/codecs.tsnow wrap only top-level numbers and booleans with Bun'ssql.json(), so Bun no longer binds them asint4/float8/bool. Strings, objects and arrays are unchanged, so nothing is double-encoded. Bun below 1.4.4 has nosql.json()and keeps the current behavior.Branch (based on
beta): https://github.com/Gourav224/drizzle-orm/tree/fix/bun-sql-jsonb-scalar-paramsCompare: beta...Gourav224:drizzle-orm:fix/bun-sql-jsonb-scalar-params
Tests: a new case in
integration-tests/tests/bun/bun-sql.test.ts(Bun 1.4.4, Postgres 17) fails onbetawithcolumn "json" is of type json but expression is of type integerand passes with the fix. The rest of that file has 218 passing; its 3 failing bigint-output tests also fail on unmodifiedbeta. Withprepare: false, top-level strings still fail, which is #6132 and unchanged here.This fix was prepared with Claude Code.