{"id":"f1686204450fd939","repo":"drizzle-team/drizzle-orm","slug":"rollback-f16862","errorCode":null,"errorMessage":"Rollback","messagePattern":"Rollback","errorType":"exception","errorClass":"TransactionRollbackError","httpStatus":null,"severity":"warning","filePath":"drizzle-orm/src/mysql-core/session.ts","lineNumber":259,"sourceCode":"\tTQueryResult extends MySqlQueryResultHKT,\n\tTPreparedQueryHKT extends PreparedQueryHKTBase,\n\tTFullSchema extends Record<string, unknown> = Record<string, never>,\n\tTSchema extends TablesRelationalConfig = Record<string, never>,\n> extends MySqlDatabase<TQueryResult, TPreparedQueryHKT, TFullSchema, TSchema> {\n\tstatic override readonly [entityKind]: string = 'MySqlTransaction';\n\n\tconstructor(\n\t\tdialect: MySqlDialect,\n\t\tsession: MySqlSession,\n\t\tprotected schema: RelationalSchemaConfig<TSchema> | undefined,\n\t\tprotected readonly nestedIndex: number,\n\t\tmode: Mode,\n\t) {\n\t\tsuper(dialect, session, schema, mode);\n\t}\n\n\trollback(): never {\n\t\tthrow new TransactionRollbackError();\n\t}\n\n\t/** Nested transactions (aka savepoints) only work with InnoDB engine. */\n\tabstract override transaction<T>(\n\t\ttransaction: (tx: MySqlTransaction<TQueryResult, TPreparedQueryHKT, TFullSchema, TSchema>) => Promise<T>,\n\t): Promise<T>;\n}\n\nexport interface PreparedQueryHKTBase extends MySqlPreparedQueryHKT {\n\ttype: MySqlPreparedQuery<Assume<this['config'], MySqlPreparedQueryConfig>>;\n}\n","sourceCodeStart":241,"sourceCodeEnd":271,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/mysql-core/session.ts#L241-L271","documentation":"A TransactionRollbackError (subclass of DrizzleError) with message 'Rollback', thrown by MySqlTransaction.rollback() (drizzle-orm/src/mysql-core/session.ts:259). Calling tx.rollback() inside a db.transaction() callback is the documented way to abort; the thrown error unwinds the callback so the driver can issue ROLLBACK. It is expected control flow, not a defect, when the developer intentionally aborts.","triggerScenarios":"Inside db.transaction(async tx => { ...; tx.rollback(); }), the explicit rollback() call throws this error to short-circuit the callback. Any caller that does not wrap the transaction in try/catch will see it propagate.","commonSituations":"A developer adds business-rule validation inside a transaction and calls tx.rollback() to cancel, but forgets that rollback() throws, so the error bubbles to an outer handler that treats it as a crash. Also common when porting code from a driver whose rollback does not throw.","solutions":["Wrap db.transaction(...) in try/catch and check for TransactionRollbackError specifically; treat it as 'aborted by app', not a failure.","Ensure your rollback is genuinely intentional; if it fired unexpectedly, find the code path that called tx.rollback().","Do not swallow all errors — only swallow TransactionRollbackError when the abort was deliberate."],"exampleFix":"// before\nawait db.transaction(async (tx) => {\n  await tx.insert(users).values(payload);\n  if (!valid) tx.rollback(); // throws TransactionRollbackError, unhandled\n});\n\n// after\nimport { TransactionRollbackError } from 'drizzle-orm/errors';\ntry {\n  await db.transaction(async (tx) => {\n    await tx.insert(users).values(payload);\n    if (!valid) tx.rollback();\n  });\n} catch (e) {\n  if (e instanceof TransactionRollbackError) return { ok: false, reason: 'validation' };\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import { TransactionRollbackError } from 'drizzle-orm/errors';\n\nfunction isIntentionalRollback(e: unknown): e is TransactionRollbackError {\n  return e instanceof TransactionRollbackError;\n}","tryCatchPattern":"try {\n  await db.transaction(async (tx) => {\n    await tx.insert(users).values(payload);\n    if (!valid) tx.rollback();\n  });\n} catch (e) {\n  if (e instanceof TransactionRollbackError) {\n    return { ok: false, reason: 'business-rule validation aborted tx' };\n  }\n  throw e; // a real error\n}","preventionTips":["Always wrap db.transaction() in try/catch and treat TransactionRollbackError as expected control flow.","Only swallow TransactionRollbackError; rethrow everything else.","Document that tx.rollback() throws, so teammates do not add a second throw handler."],"tags":["mysql","transaction","rollback","control-flow"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}