
Fix: drizzle-kit push — statement does not return data
drizzle-kit push failing with a driver-level TypeError on SQLite/libSQL/Turso setups. Why .run() and .get() aren't interchangeable, and the two safe ways around it.

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.
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() insteadThe 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, likeSELECT..run()— for statements that only report how many rows were affected, likeINSERT,UPDATE,CREATE TABLE, orPRAGMA.
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. If the database was built with push and you now switch to generate and migrate, the first migrate replays your initial migration against tables that already exist, so baseline the migration log first.
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' });npx drizzle-kit generate
node migrate.tsThis 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 pushIf 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_columnInspect 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#
npx drizzle-kit generateand read the resulting.sqlfile — confirm it contains only the change you intended, nothing extra picked up by a stale diff.- Run
node migrate.ts(or your project's equivalent) against a disposable copy of the database first, not production. - 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.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Push also stops to ask questions it can't answer alone#
Separate from the driver-method TypeError, drizzle-kit push has another source of
surprise failures worth ruling out first if the error only shows up in CI or a scripted
deploy step: when its schema diff can't tell whether a column was renamed or dropped and
re-added under a new name, it pauses and asks interactively which one you meant. In a
terminal that works fine — you pick an option and it continues. In a non-interactive shell
(a CI runner, a Docker build step, a cron job) there's no terminal to answer the prompt, so
the process either hangs until the job times out or exits with an unhelpful error that has
nothing to do with the driver-method mismatch this page otherwise covers.
The practical takeaway is the same one this page already recommends for the TypeError
case, for an unrelated reason: push's live, interactive diff is built for a human at a
keyboard during local development, not for unattended pipelines. If a deploy step needs to
apply schema changes without a person present, generate the SQL ahead of time with
drizzle-kit generate and commit the resulting file, then run migrate() against it in
CI — that step has nothing to introspect or disambiguate, so it has no prompt to hang on.
Schema introspection speed is a separate but related pain point once a project's schema
grows large enough that even generate and IDE type-checking start lagging, covered in
Fix Extremely Slow Drizzle IntelliSense on Large Schemas.
Further reading: Fix npx prisma db push Hanging on Supabase (Port 6543), Prisma Can't Connect to PostgreSQL: Fix invalid port. Also see Drizzle D1: migrations do not match any local migration.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix drizzle-kit migrate Hanging After Using 'pg' Driver
Get a real error message, or a finished migration, out of a drizzle-kit migrate that sits on its last line: two causes you can rule out in a minute, and two ways to apply the migrations anyway.
Drizzle D1: migrations do not match any local migration
Why drizzle-kit migrate on Cloudflare D1 works once and then rejects every migration it applied, with id and created_at undefined, and the version pair that makes repeat runs pass again.
drizzle-kit migrate: relation "users" already exists
Get drizzle-kit migrate working on a database that drizzle-kit push created: why the first CREATE TABLE runs again, how to mark the old migrations as applied, and why IF NOT EXISTS only hides the problem.


