{"id":"b4b262cb8daf0d08","repo":"drizzle-team/drizzle-orm","slug":"rollback-b4b262","errorCode":null,"errorMessage":"Rollback","messagePattern":"Rollback","errorType":"exception","errorClass":"TransactionRollbackError","httpStatus":null,"severity":"warning","filePath":"drizzle-orm/src/pg-core/session.ts","lineNumber":257,"sourceCode":"\tTSchema extends TablesRelationalConfig = Record<string, never>,\n> extends PgDatabase<TQueryResult, TFullSchema, TSchema> {\n\tstatic override readonly [entityKind]: string = 'PgTransaction';\n\n\tconstructor(\n\t\tdialect: PgDialect,\n\t\tsession: PgSession<any, any, any>,\n\t\tprotected schema: {\n\t\t\tfullSchema: Record<string, unknown>;\n\t\t\tschema: TSchema;\n\t\t\ttableNamesMap: Record<string, string>;\n\t\t} | undefined,\n\t\tprotected readonly nestedIndex = 0,\n\t) {\n\t\tsuper(dialect, session, schema);\n\t}\n\n\trollback(): never {\n\t\tthrow new TransactionRollbackError();\n\t}\n\n\t/** @internal */\n\tgetTransactionConfigSQL(config: PgTransactionConfig): SQL {\n\t\tconst chunks: string[] = [];\n\t\tif (config.isolationLevel) {\n\t\t\tchunks.push(`isolation level ${config.isolationLevel}`);\n\t\t}\n\t\tif (config.accessMode) {\n\t\t\tchunks.push(config.accessMode);\n\t\t}\n\t\tif (typeof config.deferrable === 'boolean') {\n\t\t\tchunks.push(config.deferrable ? 'deferrable' : 'not deferrable');\n\t\t}\n\t\treturn sql.raw(chunks.join(' '));\n\t}\n\n\tsetTransaction(config: PgTransactionConfig): Promise<void> {","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/pg-core/session.ts#L239-L275","documentation":"PgTransaction.rollback (session.ts:256) intentionally throws a TransactionRollbackError (a DrizzleError subclass named 'TransactionRollbackError' with message 'Rollback'). It is a control-flow signal: Drizzle's transaction wrapper catches it to issue ROLLBACK and abort the callback cleanly. It is not a bug — it is the supported way to abort a transaction from inside.","triggerScenarios":"Calling tx.rollback() inside a db.transaction(async (tx) => { ... }) callback when business logic decides the work should not commit. The thrown error propagates unless caught by the wrapper.","commonSituations":"Validation failure inside a transaction; optimistic concurrency conflict; partial work that must be discarded; wrapping rollback in a try/catch that accidentally swallows the signal.","solutions":["Let TransactionRollbackError propagate out of the callback so Drizzle's wrapper handles ROLLBACK.","At the db.transaction() call site, catch TransactionRollbackError to treat it as an expected abort rather than a crash.","Do not wrap tx.rollback() in a try/catch inside the callback that swallows it."],"exampleFix":"// before\ntry {\n  await db.transaction(async (tx) => {\n    if (!ok) await tx.rollback();\n  });\n} catch (e) { /* treated as crash */ }\n\n// after\ntry {\n  await db.transaction(async (tx) => {\n    if (!ok) tx.rollback();\n  });\n} catch (e) {\n  if (e instanceof TransactionRollbackError) {/* expected abort */}\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Centralize transaction abort handling at the call site.\nasync function runTx<T>(db: any, fn: (tx: any) => Promise<T>): Promise<T | null> {\n  try {\n    return await db.transaction(fn);\n  } catch (e) {\n    if (e instanceof TransactionRollbackError) return null; // expected abort\n    throw e;\n  }\n}","typeGuard":"import { TransactionRollbackError } from 'drizzle-orm/errors';\n\nfunction isRollback(e: unknown): e is TransactionRollbackError {\n  return e instanceof TransactionRollbackError;\n}","tryCatchPattern":"import { TransactionRollbackError } from 'drizzle-orm/errors';\n\ntry {\n  await db.transaction(async (tx) => {\n    if (!shouldCommit) tx.rollback();\n    await tx.insert(t).values(row);\n  });\n} catch (e) {\n  if (e instanceof TransactionRollbackError) {\n    // expected abort; nothing was committed\n  } else {\n    throw e; // real failure\n  }\n}","preventionTips":["Catch TransactionRollbackError at the db.transaction() call site, not inside the callback.","Do not swallow the error inside the transaction body.","Use rollback() as a control-flow signal, not an exceptional failure."],"tags":["transaction","rollback","control-flow"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}