phalcon/cphalcon · error · Phalcon\DataMapper\Pdo\Exception\CannotDisconnect
Cannot disconnect a Decorated connection instance
Error message
Cannot disconnect a Decorated connection instance
What it means
Connection\Decorated wraps an already-constructed \PDO instance. The wrapper does not own the underlying connection's lifecycle — connect() is a no-op ('already connected') and disconnect() always throws CannotDisconnect, because closing the shared PDO is the original owner's job.
Source
Thrown at phalcon/DataMapper/Pdo/Connection/Decorated.zep:64
this->setProfiler(profiler);
}
/**
* Connects to the database.
*/
public function connect() -> void
{
// already connected
}
/**
* Disconnects from the database; disallowed with decorated PDO connections.
*
* @throws CannotDisconnect
*/
public function disconnect() -> void
{
throw new CannotDisconnect(
"Cannot disconnect a Decorated connection instance"
);
}
}
View on GitHub (pinned to b7419de9cd)
Solutions
- Skip teardown for wrapped instances: if (!$conn instanceof Connection\Decorated) { $conn->disconnect(); }
- Let the code that created the \PDO manage its lifecycle (null the reference where it was constructed)
- In pools, drop the reference instead of calling disconnect() on decorated connections
- Make ownership explicit in design: whoever news up the \PDO destroys it
Example fix
// before
foreach ($this->connections as $conn) {
$conn->disconnect(); // CannotDisconnect on Decorated
}
// after
foreach ($this->connections as $conn) {
if (!$conn instanceof \Phalcon\DataMapper\Pdo\Connection\Decorated) {
$conn->disconnect();
}
} Defensive patterns
Strategy: type-guard
Type guard
use Phalcon\DataMapper\Pdo\Connection\Decorated;
function canDisconnect(object $connection): bool
{
return !$connection instanceof Decorated;
}
// foreach ($connections as $conn) { if (canDisconnect($conn)) $conn->disconnect(); } Try / catch
use Phalcon\DataMapper\Pdo\Connection\Decorated;
use Phalcon\DataMapper\Pdo\Exception\CannotDisconnect;
try {
$connection->disconnect();
} catch (CannotDisconnect $e) {
// wrapped PDO: lifecycle belongs to the code that created it; just drop the reference
} Prevention
- Guard teardown loops with an instanceof Decorated check before calling disconnect()
- Keep ownership rules explicit: whoever constructs the \PDO disposes it
- In pools, replace disconnect() with reference release for decorated connections
When it happens
Trigger: $decorated = new Decorated($pdo); $decorated->disconnect(); teardown/pool code that iterates all connections and calls disconnect() on each; reusing shutdown logic written for Connection against a Decorated instance.
Common situations: Connection registries or pools holding mixed Connection and Decorated instances; long-lived shared PDO handed to several components; test tear-down that disconnects everything it created.
Related errors
- Driver not supported [{driver}]
- Class '{className}' does not have a method '{name}'
- Connection not found: {type}:{requested}
- Operation cancelled by a listener of '{eventName}'
- Invalid bind parameter (1)
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/03e031b84d08baf0.
Report an issue: GitHub.