{"record":{"id":"f86cdc0c305375df","repo":"n8n-io/n8n","slug":"transaction-rollback-failed","errorCode":null,"errorMessage":"Transaction rollback failed","messagePattern":"Transaction rollback failed","errorType":"exception","errorClass":"TransactionRollbackFailedError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts","lineNumber":156,"sourceCode":"\t */\n\tasync rollbackTransaction(): Promise<void> {\n\t\ttry {\n\t\t\tif (!this.isTransactionActive) throw new TransactionNotStartedError();\n\n\t\t\tawait this.broadcaster.broadcast('BeforeTransactionRollback');\n\n\t\t\tif (this.transactionDepth > 1) {\n\t\t\t\tthis.transactionDepth -= 1;\n\t\t\t\tawait this.query(`ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`);\n\t\t\t} else {\n\t\t\t\tthis.transactionDepth -= 1;\n\t\t\t\tawait this.query('ROLLBACK');\n\t\t\t\tthis.isTransactionActive = false;\n\t\t\t}\n\n\t\t\tawait this.broadcaster.broadcast('AfterTransactionRollback');\n\t\t} catch (rollbackError) {\n\t\t\tthrow new TransactionRollbackFailedError(rollbackError);\n\t\t}\n\t}\n\n\t/**\n\t * Returns raw data stream.\n\t */\n\tstream(\n\t\tquery: string,\n\t\tparameters?: any[],\n\t\tonEnd?: Function,\n\t\tonError?: Function,\n\t): Promise<ReadStream> {\n\t\tthrow new TypeORMError(`Stream is not supported by sqlite driver.`);\n\t}\n\n\t/**\n\t * Returns all available database names including system databases.\n\t */","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts#L138-L174","documentation":"Thrown by the abstract SQLite query runner when the underlying `ROLLBACK` (or `ROLLBACK TO SAVEPOINT`) SQL statement itself fails inside rollbackTransaction(). The original driver error is passed as `error.cause` (see TransactionRollbackFailedError). It means SQLite could not undo the transaction — almost always a low-level I/O or connection-state problem, not a bug in your migration logic.","triggerScenarios":"Calling queryRunner.rollbackTransaction() on a SQLite AbstractSqliteQueryRunner after the connection hit a disk I/O error, the DB file was removed/unreachable, or an earlier statement already broke the transaction. Also when a nested savepoint rollback fails (transactionDepth > 1, e.g. 'no such savepoint: typeorm_0').","commonSituations":"SQLite DB file on an unreliable/network mount, disk full, file deleted or moved while the process runs, concurrent write-lock escalation corrupting txn state, or code that manually fiddles with the same sqlite3 connection outside TypeORM.","solutions":["Inspect `error.cause` for the real SQLite error code/message (SQLITE_IOERR, SQLITE_BUSY, 'no such savepoint', 'database is locked') and address that root cause.","Keep the DB file on stable local storage for the process lifetime.","If triggered by a failed prior statement, fix that statement so the transaction stays valid.","Re-open the DataSource / restart the process — after a failed rollback the connection is typically wedged and must not be reused."],"exampleFix":"// before\nawait queryRunner.rollbackTransaction();\n\n// after\ntry {\n  await queryRunner.rollbackTransaction();\n} catch (e) {\n  if (e instanceof TransactionRollbackFailedError) {\n    logger.error('rollback failed', { cause: e.cause });\n    await dataSource.destroy(); // connection is unusable\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// No safe pre-check: rollback fails at the SQL layer.\n// Ensure the connection is healthy before attempting:\nif (!queryRunner.connection.isInitialized) {\n  throw new Error('DataSource not initialized — cannot rollback');\n}","typeGuard":"import { TypeORMError } from '@n8n/typeorm';\n\nfunction isTransactionRollbackFailedError(e: unknown): e is TypeORMError & { cause: unknown } {\n  return e instanceof TypeORMError && e.message === 'Transaction rollback failed';\n}","tryCatchPattern":"try {\n  await queryRunner.rollbackTransaction();\n} catch (e) {\n  if (isTransactionRollbackFailedError(e)) {\n    logger.error('sqlite rollback failed', { cause: e.cause });\n    // connection is wedged — tear it down\n    await dataSource.destroy().catch(() => {});\n  }\n  throw e;\n}","preventionTips":["Keep the SQLite file on local, stable storage.","Avoid long-running transactions that increase exposure to I/O or lock failures.","Don't manipulate the underlying sqlite3 connection behind TypeORM's back.","Log error.cause — the real SQLite code is the actionable signal."],"tags":["database","transaction","sqlite"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}