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

  1. Guard the finalize step: if ($connection->isUnderTransaction()) { $connection->commit(); }
  2. 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)
  3. 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

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


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/58d2321e481df529. Report an issue: GitHub.