{"record":{"id":"ae62732841337037","repo":"n8n-io/n8n","slug":"transaction-method-requires-callback-in-second-par","errorCode":null,"errorMessage":"Transaction method requires callback in second parameter if isolation level is supplied.","messagePattern":"Transaction method requires callback in second parameter if isolation level is supplied\\.","errorType":"exception","errorClass":"TypeORMError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/entity-manager/EntityManager.ts","lineNumber":130,"sourceCode":"\t): Promise<T>;\n\n\t/**\n\t * Wraps given function execution (and all operations made there) in a transaction.\n\t * All database operations must be executed using provided entity manager.\n\t */\n\tasync transaction<T>(\n\t\tisolationOrRunInTransaction: IsolationLevel | ((entityManager: EntityManager) => Promise<T>),\n\t\trunInTransactionParam?: (entityManager: EntityManager) => Promise<T>,\n\t): Promise<T> {\n\t\tconst isolation =\n\t\t\ttypeof isolationOrRunInTransaction === 'string' ? isolationOrRunInTransaction : undefined;\n\t\tconst runInTransaction =\n\t\t\ttypeof isolationOrRunInTransaction === 'function'\n\t\t\t\t? isolationOrRunInTransaction\n\t\t\t\t: runInTransactionParam;\n\n\t\tif (!runInTransaction) {\n\t\t\tthrow new TypeORMError(\n\t\t\t\t`Transaction method requires callback in second parameter if isolation level is supplied.`,\n\t\t\t);\n\t\t}\n\n\t\tif (this.queryRunner && this.queryRunner.isReleased)\n\t\t\tthrow new QueryRunnerProviderAlreadyReleasedError();\n\n\t\t// if query runner is already defined in this class, it means this entity manager was already created for a single connection\n\t\t// if its not defined we create a new query runner - single connection where we'll execute all our operations\n\t\tconst queryRunner = this.queryRunner || this.connection.createQueryRunner();\n\n\t\ttry {\n\t\t\tawait queryRunner.startTransaction(isolation);\n\t\t\tconst result = await runInTransaction(queryRunner.manager);\n\t\t\tawait queryRunner.commitTransaction();\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\ttry {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/entity-manager/EntityManager.ts#L112-L148","documentation":"EntityManager.transaction() accepts an overload where the first argument is an IsolationLevel string and the second is the callback. If callers pass an isolation level but no callback, the runInTransaction variable is undefined and TypeORM throws a TypeORMError explaining that the callback is required when an isolation level is supplied. This is a usage contract guard, not a runtime/data condition.","triggerScenarios":"Calling `manager.transaction('SERIALIZABLE')` with no second argument; passing an isolation string and accidentally omitting the callback; refactoring a call site that previously passed only a callback and adding an isolation level without updating argument order; programmatically building the call and forgetting the callback slot.","commonSituations":"Refactor across TypeORM versions (older code had different overloads); copy-paste from examples that omit the callback; TS strictness bypassed via `any` so the compiler did not catch the missing argument; dead/conditional callback that evaluated to undefined.","solutions":["Pass the callback as the second argument: `manager.transaction('SERIALIZABLE', async (em) => { ... })`.","If you don't need a custom isolation level, drop the first argument entirely: `manager.transaction(async (em) => { ... })`.","If building the call dynamically, assert the callback is a function before invoking, and enable strict TS so the overload rejects missing args.","Add a unit test asserting transaction() is always invoked with both arguments at that call site."],"exampleFix":"// before\nawait manager.transaction('SERIALIZABLE');\n\n// after - callback supplied as second argument\nawait manager.transaction('SERIALIZABLE', async (em) => {\n  await em.save(User, { id: 1, name: 'x' });\n});","handlingStrategy":"validation","validationCode":"function assertTransactionArgs(isolation: unknown, cb: unknown): asserts cb is Function {\n  if (typeof isolation === 'string' && typeof cb !== 'function') {\n    throw new Error('manager.transaction(isolation, cb): cb is required when isolation is supplied');\n  }\n}\n// usage\nassertTransactionArgs(isolationLevel, callback);\nawait manager.transaction(isolationLevel as IsolationLevel, callback);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enable TS strict mode so transaction() overloads reject missing args.","Prefer the single-callback form unless you really need a custom isolation level.","Lint against `manager.transaction(` calls with fewer than 2 args when a string literal is the first arg."],"tags":["typeorm","transactions","api-misuse","typescript"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}