{"id":"37de15e1aa9259f6","repo":"drizzle-team/drizzle-orm","slug":"nested-transactions-are-handled-by-netlifydbtransa","errorCode":null,"errorMessage":"Nested transactions are handled by NetlifyDbTransaction via savepoints","messagePattern":"Nested transactions are handled by NetlifyDbTransaction via savepoints","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"drizzle-orm/src/netlify-db/session.ts","lineNumber":263,"sourceCode":"\t\t\trowMode: 'array',\n\t\t\ttext: query,\n\t\t\tvalues: params,\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync queryObjects<T extends QueryResultRow>(\n\t\tquery: string,\n\t\tparams: unknown[],\n\t): Promise<QueryResult<T>> {\n\t\treturn this.client.query<T>(query, params);\n\t}\n\n\toverride async transaction<T>(\n\t\t_transaction: (tx: NetlifyDbTransaction<TFullSchema, TSchema>) => Promise<T>,\n\t\t_config?: PgTransactionConfig,\n\t): Promise<T> {\n\t\tthrow new Error('Nested transactions are handled by NetlifyDbTransaction via savepoints');\n\t}\n}\n\nexport class NetlifyDbTransaction<\n\tTFullSchema extends Record<string, unknown>,\n\tTSchema extends V1.TablesRelationalConfig,\n> extends PgTransaction<NeonHttpQueryResultHKT, TFullSchema, TSchema> {\n\tstatic override readonly [entityKind]: string = 'NetlifyDbTransaction';\n\n\toverride async transaction<T>(\n\t\ttransaction: (tx: NetlifyDbTransaction<TFullSchema, TSchema>) => Promise<T>,\n\t): Promise<T> {\n\t\tconst savepointName = `sp${this.nestedIndex + 1}`;\n\t\tconst tx = new NetlifyDbTransaction<TFullSchema, TSchema>(\n\t\t\tthis.dialect,\n\t\t\tthis.session,\n\t\t\tthis.schema,\n\t\t\tthis.nestedIndex + 1,","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/netlify-db/session.ts#L245-L281","documentation":"An Error 'Nested transactions are handled by NetlifyDbTransaction via savepoints' thrown by NetlifyDbWsSession.transaction() (drizzle-orm/src/netlify-db/session.ts:263). This is an internal guard: NetlifyDbSession.transaction() opens a real transaction using an internal WebSocket session (NetlifyDbWsSession), and nested transactions inside it must go through NetlifyDbTransaction.transaction() (which emits SAVEPOINTs). If something calls the WsSession's transaction() directly, it throws to redirect to the savepoint path.","triggerScenarios":"Only reachable through internal misuse — calling transaction() on the private NetlifyDbWsSession rather than on the NetlifyDbTransaction handed to the db.transaction() callback. Normal nested usage (tx.transaction(...) inside db.transaction()) hits NetlifyDbTransaction.transaction() and works correctly via savepoints.","commonSituations":"Not a user-facing error in correct usage. Would appear only in tests or custom code that grabs the internal session object and calls .transaction() on it. Seeing it means the wrong transaction entry point was used.","solutions":["Call tx.transaction(...) on the NetlifyDbTransaction passed to your callback, never on db.session.","Do not reach into internal session objects; use the public db.transaction() / tx.transaction() API.","If it surfaces from library code, report it — the public API should route nesting through NetlifyDbTransaction."],"exampleFix":"// before (misuse of internals)\nawait (db as any).session.transaction(async (sp) => { /* ... */ }); // throws — wrong entry point\n\n// after — nest via the handed-back transaction object (savepoints)\nawait db.transaction(async (tx) => {\n  await tx.transaction(async (sp) => { /* SAVEPOINT sp1 */ });\n});","handlingStrategy":"validation","validationCode":"import { NetlifyDbWsSession } from 'drizzle-orm/netlify-db/session';\n\nfunction isInternalWsSession(s: unknown): boolean {\n  return s instanceof NetlifyDbWsSession;\n}\n\n// ensure nesting goes through the transaction object, never db.session\nfunction assertNotInternalSession(target: unknown) {\n  if (isInternalWsSession(target)) {\n    throw new Error('Call tx.transaction() on the NetlifyDbTransaction, not on the internal session.');\n  }\n}","typeGuard":"import { NetlifyDbWsSession } from 'drizzle-orm/netlify-db/session';\n\nfunction isNetlifyWsSession(s: unknown): s is NetlifyDbWsSession<any, any> {\n  return s instanceof NetlifyDbWsSession;\n}","tryCatchPattern":null,"preventionTips":["Use only the public API: db.transaction(tx => ...), and nest via tx.transaction(...).","Never reach into db.session for transactional calls.","If this surfaces from library code, report it as a bug."],"tags":["netlify-db","transaction","nested","savepoint","internal"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}