phalcon/cphalcon · error · Phalcon\Db\Exceptions\NoActiveTransaction
There is no active transaction
Error message
There is no active transaction
What it means
commit() requires the adapter's internal transactionLevel to be at least 1; level 0 means no begin() is outstanding on this adapter instance and NoActiveTransaction is thrown. Each adapter object tracks its own nesting counter, so the check is bookkeeping-local: it does not ask the server whether a transaction is open.
Source
Thrown at phalcon/Db/Adapter/Pdo/AbstractPdo.zep:169
if typeof eventsManager == "object" {
eventsManager->fire("db:createSavepoint", this, savepointName);
}
return this->createSavepoint(savepointName);
}
/**
* Commits the active transaction in the connection
*/
public function commit(bool nesting = true) -> bool
{
var eventsManager, savepointName;
/**
* Check the transaction nesting level
*/
if this->transactionLevel === 0 {
throw new NoActiveTransaction();
}
if this->transactionLevel === 1 {
/**
* Notify the events manager about the committed transaction
*/
let eventsManager = <ManagerInterface> this->eventsManager;
if typeof eventsManager == "object" {
eventsManager->fire("db:commitTransaction", this);
}
/**
* Reduce the transaction nesting level
*/
let this->transactionLevel--;
return this->pdo->commit();
}View on GitHub (pinned to b7419de9cd)
Solutions
- Guard the finalize step: if ($connection->isUnderTransaction()) { $connection->commit(); }
- Structure the unit of work so exactly one of commit/rollback executes per begin() (commit in try is an anti-pattern — commit at the end of try, rollback in catch, and never both)
- Confirm begin() succeeded (it returns bool) before doing work that will be committed
Example fix
// before
try {
$connection->begin();
work($connection);
} catch (\Throwable $e) {
$connection->rollback();
throw $e;
} finally {
$connection->commit(); // runs after rollback too -> throws
}
// after
$connection->begin();
try {
work($connection);
$connection->commit();
} catch (\Throwable $e) {
if ($connection->isUnderTransaction()) {
$connection->rollback();
}
throw $e;
} Defensive patterns
Strategy: validation
Validate before calling
if ($connection->isUnderTransaction()) {
$connection->commit();
} else {
$logger->warning('commit() skipped: no active transaction');
} Try / catch
use Phalcon\Db\Exceptions\NoActiveTransaction;
try {
$connection->commit();
} catch (NoActiveTransaction $e) {
// already finalized elsewhere — log and continue
$logger->warning($e->getMessage());
} Prevention
- Commit at the end of try, rollback in catch — never in finally — so only one finalize runs per begin()
- Guard finalization with isUnderTransaction() / getTransactionLevel() > 0 in any wrapper that may double-finalize
- Check begin()'s boolean return before doing transactional work
When it happens
Trigger: commit() without a prior successful begin(); double commit (commit, then commit again in a finally block); rollback already ran in the catch path, decremented the level to 0, and a finally then calls commit; calling commit() on a different adapter instance than the one that called begin().
Common situations: finally blocks that both roll back on error and commit on success, arranged so both run; layered transaction wrappers (service + repository each finalizing); long-lived workers where a previous exception already unwound the transaction; tests that reuse an adapter across cases without resetting state.
Related errors
- Savepoints are not supported by this database adapter
- Nested transaction with savepoints behavior cannot be change
- The 'dialectClass' '{className}' must implement Phalcon\Db\D
- The table must contain at least one column
- Unable to insert into {table} without data
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/58d2321e481df529.
Report an issue: GitHub.