{"record":{"id":"f399e6e54ad9df99","repo":"remix-run/remix","slug":"unknown-transaction-token-token-id","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-mysql/src/lib/driver.ts","lineNumber":262,"sourceCode":"\n    this.#transactions.set(token.id, {\n      connection,\n      releaseOnClose,\n    })\n\n    return token\n  }\n\n  /**\n   * Commits an open mysql transaction.\n   * @param token Transaction token to commit.\n   * @returns A promise that resolves when the transaction is committed.\n   */\n  async commitTransaction(token: TransactionToken): Promise<void> {\n    let transaction = this.#transactions.get(token.id)\n\n    if (!transaction) {\n      throw new Error('Unknown transaction token: ' + token.id)\n    }\n\n    let failed = false\n    try {\n      await transaction.connection.commit()\n    } catch (error) {\n      failed = true\n      throw error\n    } finally {\n      this.#transactions.delete(token.id)\n\n      if (transaction.releaseOnClose) {\n        if (failed) {\n          destroyMysqlConnection(transaction.connection)\n        } else if (isMysqlPoolConnection(transaction.connection)) {\n          transaction.connection.release()\n        }\n      }","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table-mysql/src/lib/driver.ts#L244-L280","documentation":"The MySQL data-table driver tracks open transactions in an internal map keyed by transaction token id. commitTransaction throws when asked to commit a token that is not in that map — typically because the transaction was already committed, rolled back, or the token was fabricated or from another driver instance.","triggerScenarios":"Calling driver.commitTransaction(token) twice with the same token; committing after rollbackTransaction already removed it; passing a TransactionToken obtained from a different driver instance or constructed manually; committing after the transaction errored out and auto-removed itself.","commonSituations":"Retry logic that re-commits on failure without checking state; long-lived token references held across driver restarts (e.g. pool closed and reopened); concurrent code paths both finalizing the same transaction.","solutions":["Commit or roll back each token exactly once; clear/ignore the token after finalization","Ensure the token comes from the same driver instance that began the transaction","Wrap begin/commit in a helper that guarantees single finalization (commit on success, rollback on error)"],"exampleFix":"// before\nawait driver.commitTransaction(token)\n// ...later, retry path\nawait driver.commitTransaction(token) // throws\n// after\nlet done = false\ntry {\n  await driver.commitTransaction(token)\n  done = true\n} finally {\n  token = done ? token : undefined\n}","handlingStrategy":"try-catch","validationCode":"// Track finalization yourself\nlet finalized = false\n// only commit when not finalized: if (!finalized) await driver.commitTransaction(token)","typeGuard":"type FinalizableToken = TransactionToken & { finalized?: boolean }\nconst canCommit = (t: FinalizableToken) => !t.finalized","tryCatchPattern":"try { await driver.commitTransaction(token) } catch (e) { if (e instanceof Error && e.message.startsWith('Unknown transaction token')) { /* already finalized: ignore */ } else throw e }","preventionTips":["Commit/rollback exactly once per token; use a wrapper helper","Discard token references after finalization"],"tags":["data-table","mysql","transaction","token"],"backgroundTag":"unknown-transaction-token","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}