Skip to content

fix(durable-sqlite): surface migration error via Error.cause on rollback#5776

Draft
spokodev wants to merge 1 commit into
drizzle-team:mainfrom
spokodev:fix/durable-sqlite-migrator-cause
Draft

fix(durable-sqlite): surface migration error via Error.cause on rollback#5776
spokodev wants to merge 1 commit into
drizzle-team:mainfrom
spokodev:fix/durable-sqlite-migrator-cause

Conversation

@spokodev
Copy link
Copy Markdown

Fixes #5763.

In the durable-sqlite migrator, tx.rollback() throws a sentinel TransactionRollbackError to abort the surrounding transaction. The previous code looked like:

```ts
} catch (error: any) {
tx.rollback(); // throws TransactionRollbackError
throw error; // never reached
}
```

Because `tx.rollback()` throws synchronously, the `throw error` below it never executes. The caller only ever saw the generic `"Rollback"` message — the real cause (failed SQL statement, schema mismatch, etc.) was discarded. This matched the symptom @Craig-J described in #5763.

The fix wraps the rollback in a try/catch and attaches the original migration error as `rollbackError.cause` before re-throwing, so callers can recover the underlying failure:

```ts
} catch (error: any) {
try {
tx.rollback();
} catch (rollbackError: any) {
rollbackError.cause = error;
throw rollbackError;
}
throw error;
}
```

Behaviour change

Standalone verification (mocking the tx interface):

```
--- before ---
caller sees: TransactionRollbackError / Rollback
caller sees .cause: undefined

--- after ---
caller sees: TransactionRollbackError / Rollback
caller sees .cause: Migration 0003 failed: column "x" does not exist
```

Test plan

  • `pnpm test:types` clean
  • Manual repro via a standalone script reproducing the mocked transaction/rollback flow (script not committed; behaviour above)
  • No durable-sqlite test infra exists in the repo; a Workers-runtime test would require miniflare and is out of scope for this fix

`tx.rollback()` throws a sentinel `TransactionRollbackError` to abort the
surrounding transaction. The previous code called `tx.rollback()` first
and then `throw error`, so the second throw never executed and the
caller only ever saw the generic "Rollback" message — the real cause
(e.g. failed SQL statement) was lost.

Wrap the rollback in a try and attach the original migration error as
`rollbackError.cause` before re-throwing. Callers can now read
`err.cause` to recover the underlying failure.

Fixes drizzle-team#5763
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: durable-sqlite Rollback exception hides migration failure exception

1 participant