typeorm/typeorm · error · TransactionNotStartedError
Transaction is not started yet, start transaction before com
Error message
Transaction is not started yet, start transaction before committing or rolling it back.
What it means
Thrown as TransactionNotStartedError by AuroraPostgresQueryRunner.commitTransaction when this.isTransactionActive is false — i.e. commitTransaction() was called before startTransaction() completed, or after the transaction already committed/rolled back. The guard prevents committing a non-existent transaction against the underlying client.
Source
Thrown at src/driver/aurora-postgres/AuroraPostgresQueryRunner.ts:149
this.isTransactionActive = false
}
throw err
}
}
} else {
await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
}
this.transactionDepth += 1
await this.broadcaster.broadcast("AfterTransactionStart")
}
/**
* Commits transaction.
* Error will be thrown if transaction was not started.
*/
async commitTransaction(): Promise<void> {
if (!this.isTransactionActive) throw new TransactionNotStartedError()
await this.broadcaster.broadcast("BeforeTransactionCommit")
if (this.transactionDepth > 1) {
await this.query(
`RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
)
} else {
await this.client.commitTransaction()
this.isTransactionActive = false
}
this.transactionDepth -= 1
await this.broadcaster.broadcast("AfterTransactionCommit")
}
/**
* Rollbacks transaction.View on GitHub (pinned to 04ff4daedc)
Solutions
- Always pair startTransaction() with commit/rollback and gate commit on isTransactionActive.
- Use the DataSource.transaction(async runner => ...) helper which manages the lifecycle for you.
- Reset/track transaction state in your wrapper so commit is only called once.
Example fix
// before
await queryRunner.commitTransaction() // no startTransaction first
// after
await queryRunner.startTransaction()
try { /* work */ await queryRunner.commitTransaction() }
catch { await queryRunner.rollbackTransaction() } Defensive patterns
Strategy: validation
Validate before calling
if (queryRunner.isTransactionActive) await queryRunner.commitTransaction()
Type guard
const canCommit = (qr: QueryRunner): boolean => qr.isTransactionActive
Try / catch
try { await qr.commitTransaction() } catch (e) { if (e instanceof TransactionNotStartedError) { /* nothing to commit */ } else throw e } Prevention
- Prefer DataSource.transaction(...) over manual start/commit.
- Always gate commit/rollback on isTransactionActive.
- Track started-state in wrappers to avoid double commit.
When it happens
Trigger: Calling queryRunner.commitTransaction() without a preceding startTransaction(); committing twice (second call after the first set isTransactionActive=false); committing inside a code path where startTransaction was skipped due to an early return.
Common situations: Helper functions that wrap commit but are called from both inside and outside an existing transaction; error-handling code that commits in a finally block even when startTransaction threw; misordered await chains where startTransaction() is not awaited.
Related errors
- Transaction is not started yet, start transaction before com
- Query runner already released. Cannot run queries anymore.
- Your driver does not support named placeholders.
- aurora-postgres driver does not support change comment.
- Query runner already released. Cannot run queries anymore.
AI-assisted analysis of typeorm/typeorm@04ff4daedc (2026-08-03).
Data as JSON: /data/errors/562c20268695d1c5.json.
Report an issue: GitHub.