Skip to content

drizzle-kit push fails with Turso/libSQL on table recreation: "cannot commit - no transaction is active" #5489

@sethgw

Description

@sethgw

Bug Report

Version: drizzle-kit@1.0.0-beta.17-67b1795 (also reproducible on recent beta releases)
Database: Turso (libSQL over HTTP)
Dialect: turso

Description

drizzle-kit push fails when a schema change requires table recreation (e.g., changing a foreign key's onDelete from CASCADE to SET NULL). SQLite doesn't support ALTER TABLE for FK changes, so drizzle-kit recreates the table — but the transaction wrapping breaks on Turso's HTTP protocol.

Error

QueryError: SQLITE_UNKNOWN: SQLite error: cannot commit - no transaction is active
    sql: 'commit'

Root Cause

In src/cli/commands/push-sqlite.ts, the push handler wraps DDL statements in a manual begin/commit transaction:

if (!isD1) await db.run("begin");
try {
    for (const statement of [...lossStatements, ...sqlStatements]) {
        await db.run(statement);
    }
    if (!isD1) await db.run("commit");
} catch (e) {
    if (!isD1) await db.run("rollback");
}

The D1 check (isD1) correctly skips transactions for Cloudflare D1's HTTP protocol. However, Turso/libSQL's HTTP client (@libsql/client) has the same limitation — client.execute("begin") / client.execute("commit") don't work as expected because each execute() call is an independent HTTP request with no shared transaction context.

The connectToLibSQL function already provides a batchWithPragma method that wraps client.migrate(), which is the correct API for running DDL batches. The push handler should use this instead of manual transaction control for libSQL connections.

Note: The DDL statements themselves do execute successfully (they auto-commit individually), so the schema change is applied — but the subsequent commit call fails and the process exits with an error code, which is misleading.

Workaround

Patch connectToLibSQL's run method to skip transaction control statements:

run: async (query) => {
    if (/^\s*(begin|commit|rollback)\s*$/i.test(typeof query === 'string' ? query : query.sql || '')) return;
    await client.execute(query).catch((e) => {
        throw new QueryError(e, query, []);
    });
},

Expected Behavior

drizzle-kit push with dialect: "turso" should handle table recreations without erroring on transaction management, just as it does for D1.

Steps to Reproduce

  1. Configure drizzle-kit with dialect: "turso" pointing to a Turso database
  2. Create a schema with a foreign key using onDelete: "cascade"
  3. Run drizzle-kit push to sync
  4. Change the FK to onDelete: "set null"
  5. Run drizzle-kit push again — fails with the transaction error

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions