phalcon/cphalcon · error · Phalcon\Db\Exceptions\NestedTransactionChangeBlocked
Nested transaction with savepoints behavior cannot be change
Error message
Nested transaction with savepoints behavior cannot be changed while a transaction is open
What it means
setNestedTransactionsWithSavepoints() may only be called when the adapter's transactionLevel is 0. Switching savepoint behavior while a transaction is open would corrupt the adapter's nesting bookkeeping (which levels were started with or without savepoints), so NestedTransactionChangeBlocked is thrown. The flag is a setup-time setting, not a runtime toggle.
Source
Thrown at phalcon/Db/Adapter/AbstractAdapter.zep:1180
{
let this->eventsManager = eventsManager;
}
/**
* Sets the dialect used to produce the SQL
*/
public function setDialect(<DialectInterface> dialect)
{
let this->dialect = dialect;
}
/**
* Set if nested transactions should use savepoints
*/
public function setNestedTransactionsWithSavepoints(bool nestedTransactionsWithSavepoints) -> <AdapterInterface>
{
if unlikely this->transactionLevel > 0 {
throw new NestedTransactionChangeBlocked();
}
if unlikely !this->dialect->supportsSavePoints() {
throw new SavepointsNotSupported();
}
let this->transactionsWithSavepoints = nestedTransactionsWithSavepoints;
return this;
}
/**
* Enables/disables options in the Database component.
*
* The flags are stored as process-global `Phalcon\Support\Settings`
* (`db.escape_identifiers`, `db.force_casting`) and therefore affect every
* connection in the process at once, last-writer-wins. Call this once at
* bootstrap; it is not per-connection configuration. Because theView on GitHub (pinned to b7419de9cd)
Solutions
- Configure the flag immediately after constructing the adapter, before any begin()
- If configuration is lazy, guard it: if ($connection->getTransactionLevel() === 0) { $connection->setNestedTransactionsWithSavepoints(true); }
- Move adapter configuration out of request-time code into a dedicated bootstrap step
Example fix
// before $connection->begin(); $connection->setNestedTransactionsWithSavepoints(true); // throws // after $connection->setNestedTransactionsWithSavepoints(true); // once, at bootstrap $connection->begin();
Defensive patterns
Strategy: validation
Validate before calling
if ($connection->getTransactionLevel() === 0) {
$connection->setNestedTransactionsWithSavepoints($enable);
} else {
// defer until the current unit of work closes
$deferredFlags[] = $enable;
} Try / catch
use Phalcon\Db\Exceptions\NestedTransactionChangeBlocked;
try {
$connection->setNestedTransactionsWithSavepoints($enable);
} catch (NestedTransactionChangeBlocked $e) {
$logger->warning('Savepoint mode change deferred until transaction closes');
} Prevention
- Configure setNestedTransactionsWithSavepoints() once, immediately after adapter construction
- Keep adapter configuration out of request handlers, event listeners, and lazy initializers that can run inside transactions
- If configuration is dynamic, always gate it on getTransactionLevel() === 0
When it happens
Trigger: Calling $connection->setNestedTransactionsWithSavepoints(true) after begin() and before commit()/rollback(); lazy bootstrapping code (DI factory, event listener, model initializer) that configures the adapter on first use, which happens to run inside an open transaction.
Common situations: Adapter setup placed in a shared factory that also serves transactional requests; middleware or ORM event hooks that toggle savepoint mode; deferred/lazy connection services configured at first query — inside someone else's transaction.
Related errors
- Savepoints are not supported by this database adapter
- The 'dialectClass' '{className}' must implement Phalcon\Db\D
- There is no active transaction
- Configuration file {fileName} cannot be loaded
- The table must contain at least one column
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/d584390be624e2d6.
Report an issue: GitHub.