remix-run/remix · error · Error
Unknown transaction token: ' + token.id
Error message
Unknown transaction token: ' + token.id
What it means
The internal #assertTransaction guard runs before any token-scoped SQLite operation (execute, executeScript, hasTable, hasColumn, commit, rollback) and throws when the token is unknown to this driver — the transaction already finished, the driver was closed and reopened state, or the token belongs elsewhere.
Source
Thrown at packages/data-table-sqlite/src/lib/driver.ts:464
#configOrThrow(method: string): SqliteDatabaseConfig {
if (!this.#config) {
throw new Error('SQLite database ' + method + '() requires config-based construction')
}
return this.#config
}
#assertNoOpenTransactions(method: string): void {
if (this.#transactions.size > 0) {
throw new Error('SQLite database cannot ' + method + ' while transactions are open')
}
}
#assertTransaction(token: TransactionToken): void {
this.#assertDatabaseOpen()
if (!this.#transactions.has(token.id)) {
throw new Error('Unknown transaction token: ' + token.id)
}
}
#assertDatabaseOpen(): void {
if (!this.#databaseOpen) {
throw new Error('SQLite database is closed')
}
}
}
const REMOVE_RETRIES = 10
const REMOVE_RETRY_DELAY_MS = 100
async function removeDatabaseFile(filename: string): Promise<void> {
// Windows keeps a just-closed database file locked for a short window (deferred handle
// release, antivirus scans), so removal is retried with a linear backoff
for (let attempt = 0; ; attempt++) {
try {View on GitHub (pinned to 9696913134)
Solutions
- Use the managed transaction API so token lifetime is enforced by scope
- Clear token references after commit/rollback; make cleanup idempotent by catching unknown-token errors
- Only use tokens issued by the same driver instance in the same session
Example fix
// before
try { await db.commitTransaction(tx) }
catch { await db.rollbackTransaction(tx) } // may throw 'Unknown transaction token'
// after
try { await db.commitTransaction(tx) }
catch { await db.rollbackTransaction(tx).catch(() => undefined) } Defensive patterns
Strategy: try-catch
Try / catch
await db.rollbackTransaction(tx).catch((e) => { if (!(e instanceof Error && e.message.startsWith('Unknown transaction token'))) throw e }) Prevention
- Clear token references after finishing a transaction
- Use the managed transaction callback API
- Scope token usage to a single driver instance and session
When it happens
Trigger: Using a token after commit/rollback consumed it; passing a token to a different SqliteDatabase instance; calling token-scoped methods after the database was closed (the assert checks open state first, so a closed DB reports 'SQLite database is closed' instead).
Common situations: Double rollback in catch+finally; fire-and-forget queries outliving the transaction; sharing tokens across modules/tests; retries that reuse stale tokens.
Related errors
- Unknown transaction token: + token.id
- SQLite database cannot ' + method + ' while transactions are
- Unknown transaction token: + token.id
- Postgres database cannot + method + while transactions are
- SQLite config-based construction requires node:sqlite (Node.
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/d48c3ab18c1ceee3.
Report an issue: GitHub.