{"record":{"id":"d48c3ab18c1ceee3","repo":"remix-run/remix","slug":"unknown-transaction-token-token-id-d48c3a","errorCode":null,"errorMessage":"Unknown transaction token: ' + token.id","messagePattern":"Unknown transaction token: ' \\+ token\\.id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/data-table-sqlite/src/lib/driver.ts","lineNumber":464,"sourceCode":"\n  #configOrThrow(method: string): SqliteDatabaseConfig {\n    if (!this.#config) {\n      throw new Error('SQLite database ' + method + '() requires config-based construction')\n    }\n\n    return this.#config\n  }\n\n  #assertNoOpenTransactions(method: string): void {\n    if (this.#transactions.size > 0) {\n      throw new Error('SQLite database cannot ' + method + ' while transactions are open')\n    }\n  }\n\n  #assertTransaction(token: TransactionToken): void {\n    this.#assertDatabaseOpen()\n    if (!this.#transactions.has(token.id)) {\n      throw new Error('Unknown transaction token: ' + token.id)\n    }\n  }\n\n  #assertDatabaseOpen(): void {\n    if (!this.#databaseOpen) {\n      throw new Error('SQLite database is closed')\n    }\n  }\n}\n\nconst REMOVE_RETRIES = 10\nconst REMOVE_RETRY_DELAY_MS = 100\n\nasync function removeDatabaseFile(filename: string): Promise<void> {\n  // Windows keeps a just-closed database file locked for a short window (deferred handle\n  // release, antivirus scans), so removal is retried with a linear backoff\n  for (let attempt = 0; ; attempt++) {\n    try {","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table-sqlite/src/lib/driver.ts#L446-L482","documentation":"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.","triggerScenarios":"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).","commonSituations":"Double rollback in catch+finally; fire-and-forget queries outliving the transaction; sharing tokens across modules/tests; retries that reuse stale tokens.","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"],"exampleFix":"// before\ntry { await db.commitTransaction(tx) }\ncatch { await db.rollbackTransaction(tx) } // may throw 'Unknown transaction token'\n\n// after\ntry { await db.commitTransaction(tx) }\ncatch { await db.rollbackTransaction(tx).catch(() => undefined) }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"await db.rollbackTransaction(tx).catch((e) => { if (!(e instanceof Error && e.message.startsWith('Unknown transaction token'))) throw e })","preventionTips":["Clear token references after finishing a transaction","Use the managed transaction callback API","Scope token usage to a single driver instance and session"],"tags":["sqlite","transaction","token","driver"],"backgroundTag":"invalid-transaction-token","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}