Skip to content

[BUG]: bun-sql: top-level number/boolean values fail to insert into jsonb columns in 1.0.0-rc.5 #6591

Open

Description

@tutinoko2048

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 number

So 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

  1. Gourav224 commented on Oct 25, 2026

    @Gourav224

    I have a fix on my fork. Opening a PR is currently restricted on this repo, so I'm linking it here.

    The json/jsonb codecs in drizzle-orm/src/bun-sql/postgres/codecs.ts now wrap only top-level numbers and booleans with Bun's sql.json(), so Bun no longer binds them as int4/float8/bool. Strings, objects and arrays are unchanged, so nothing is double-encoded. Bun below 1.4.4 has no sql.json() and keeps the current behavior.

    Branch (based on beta): https://github.com/Gourav224/drizzle-orm/tree/fix/bun-sql-jsonb-scalar-params
    Compare: 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 on beta with column "json" is of type json but expression is of type integer and passes with the fix. The rest of that file has 218 passing; its 3 failing bigint-output tests also fail on unmodified beta. With prepare: false, top-level strings still fail, which is #6132 and unchanged here.

    This fix was prepared with Claude Code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingbug/fixed-in-betaThis bug has been fixed in beta (or will be soon).

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions