n8n-io/n8n · error · TypeORMError
Transaction method requires callback in second parameter if
Error message
Transaction method requires callback in second parameter if isolation level is supplied.
What it means
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.
Source
Thrown at packages/@n8n/typeorm/src/entity-manager/EntityManager.ts:130
): Promise<T>;
/**
* Wraps given function execution (and all operations made there) in a transaction.
* All database operations must be executed using provided entity manager.
*/
async transaction<T>(
isolationOrRunInTransaction: IsolationLevel | ((entityManager: EntityManager) => Promise<T>),
runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
): Promise<T> {
const isolation =
typeof isolationOrRunInTransaction === 'string' ? isolationOrRunInTransaction : undefined;
const runInTransaction =
typeof isolationOrRunInTransaction === 'function'
? isolationOrRunInTransaction
: runInTransactionParam;
if (!runInTransaction) {
throw new TypeORMError(
`Transaction method requires callback in second parameter if isolation level is supplied.`,
);
}
if (this.queryRunner && this.queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
// if query runner is already defined in this class, it means this entity manager was already created for a single connection
// if its not defined we create a new query runner - single connection where we'll execute all our operations
const queryRunner = this.queryRunner || this.connection.createQueryRunner();
try {
await queryRunner.startTransaction(isolation);
const result = await runInTransaction(queryRunner.manager);
await queryRunner.commitTransaction();
return result;
} catch (err) {
try {View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before
await manager.transaction('SERIALIZABLE');
// after - callback supplied as second argument
await manager.transaction('SERIALIZABLE', async (em) => {
await em.save(User, { id: 1, name: 'x' });
}); Defensive patterns
Strategy: validation
Validate before calling
function assertTransactionArgs(isolation: unknown, cb: unknown): asserts cb is Function {
if (typeof isolation === 'string' && typeof cb !== 'function') {
throw new Error('manager.transaction(isolation, cb): cb is required when isolation is supplied');
}
}
// usage
assertTransactionArgs(isolationLevel, callback);
await manager.transaction(isolationLevel as IsolationLevel, callback); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Transactions aren't supported by ${this.connection.driver.op
- SQLite only supports SERIALIZABLE and READ UNCOMMITTED isola
- Database connection provided by a query runner was already r
- Column "${columnName}" was not found in table "${metadata.na
- You must provide selection conditions in order to find a sin
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/ae62732841337037.
Report an issue: GitHub.