DEV Community

Cover image for Fix: drizzle-kit push — statement does not return data
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on Originally published at iloveblogs.blog

Fix: drizzle-kit push — statement does not return data

TL;DR

TypeError: This statement does not return data. Use run() instead from npx drizzle-kit push is a driver-level mismatch, not a schema error: the SQLite family of drivers splits query execution into methods for statements that return rows (.get(), .all()) and methods for statements that don't (.run()), and drizzle-kit push's internal introspection calls the wrong one for a step in your migration. Skip straight to the fix if you just need the error gone.

  • Symptom: npx drizzle-kit push throws mid-diff, right after you confirm the schema changes to apply
  • Root cause: A .get()/.all() call hit a statement (often DDL) that only supports .run() on your SQLite driver
  • Fix: Update drizzle-kit to the latest release first — this class of bug gets patched driver-by-driver — and fall back to drizzle-kit generate + migrate() if push still misbehaves on your setup
  • Why generate is safer long-term: it produces reviewable SQL files instead of a live, driver-dependent diff

The error, and where it comes from

The clearest report of this is drizzle-team/drizzle-orm#2766, a 20-reaction GitHub issue: running npx drizzle-kit push and confirming the prompt to apply schema changes throws immediately, with no SQL syntax error and no indication of which table or column is at fault.

TypeError: This statement does not return data. Use run() instead
Enter fullscreen mode Exit fullscreen mode

The message is coming from the SQLite driver itself, not from your schema — Drizzle is a thin layer over drivers like better-sqlite3, @libsql/client (used for Turso), and bun:sqlite, and those drivers distinguish between two families of prepared-statement methods:

  • .get() / .all() — for statements expected to return rows, like SELECT.
  • .run() — for statements that only report how many rows were affected, like INSERT, UPDATE, CREATE TABLE, or PRAGMA.

Calling .get() against a statement the driver knows returns no rows is exactly what raises this TypeError, and it happens inside drizzle-kit's own introspection/diffing logic — the step that reads your current database schema to compute what SQL to run — not inside your application code.

Why push is more fragile than generate

drizzle-kit push works by connecting live to your database, introspecting the current schema, diffing it against your Drizzle schema definitions, and executing the resulting statements directly — all in one step, with no intermediate file. That live introspection path has to call the right driver method for every kind of statement it might encounter, across every supported SQLite driver, and a mismatch in any one combination surfaces as this exact error.

drizzle-kit generate, by contrast, only reads your TypeScript schema and writes plain .sql migration files to disk — it does not need to introspect a live connection, so this specific class of driver-method bug has nothing to trigger on. You then apply those files explicitly:

// migrate.ts
import { drizzle } from 'drizzle-orm/libsql';
import { migrate } from 'drizzle-orm/libsql/migrator';
import { createClient } from '@libsql/client';

const client = createClient({ url: process.env.DATABASE_URL! });
const db = drizzle(client);

await migrate(db, { migrationsFolder: './drizzle' });
Enter fullscreen mode Exit fullscreen mode
npx drizzle-kit generate
node migrate.ts
Enter fullscreen mode Exit fullscreen mode

This two-step flow is also what Drizzle's own documentation recommends for production and CI/CD — push is positioned as the fast, iterative option for local prototyping specifically because it skips the review step a generated SQL file gives you.

The fix: two ways around it

1. Update drizzle-kit first

This class of bug — a driver method mismatch surfaced by push's introspection — is exactly the kind of thing the Drizzle team patches per SQLite driver as reports come in. Before changing your workflow, rule out that you're simply on a version predating the fix for your specific driver:

npm install drizzle-kit@latest drizzle-orm@latest
npx drizzle-kit push
Enter fullscreen mode Exit fullscreen mode

If your driver package itself (@libsql/client, better-sqlite3, or the Bun runtime's built-in bun:sqlite) is old, update that too — the method contract between Drizzle and the driver has to match on both sides.

2. Fall back to generate + migrate()

If push still throws after updating, switch that one schema change to the file-based flow above. This isn't a downgrade — it's the same command real deployments already use, since push's live-diff convenience is a local-dev feature that most teams disable for anything shared:

npx drizzle-kit generate --name add_new_column
Enter fullscreen mode Exit fullscreen mode

Inspect the generated .sql file in ./drizzle/ before running it — this also catches any unintended drop or rewrite the diff engine inferred, which push would have executed without showing you first.

If you're specifically on Turso / libSQL

Turso's hosted libSQL and the local @libsql/client file mode have historically diverged slightly in which statements support .get() versus requiring .run(), since libSQL's remote HTTP protocol and its embedded/local mode aren't identical implementations of SQLite's C API. If the error only reproduces against a remote Turso database and not against a local .db file, that divergence — not your schema — is the more likely trigger, and the generate + migrate() path sidesteps it entirely because it never depends on live introspection over the network.

Verifying the fix

  1. npx drizzle-kit generate and read the resulting .sql file — confirm it contains only the change you intended, nothing extra picked up by a stale diff.
  2. Run node migrate.ts (or your project's equivalent) against a disposable copy of the database first, not production.
  3. Confirm the table/column exists afterward with a direct query, rather than trusting a silent exit code — migrate() throws on failure, but a partially-applied multi-statement migration is worth checking by hand once.

Related Articles


Originally published at https://www.iloveblogs.blog

Top comments (0)