symfony/symfony · error · Symfony\Component\Cache\Exception\InvalidArgumentException
"%s" requires PDO error mode attribute be set to throw Excep
Error message
"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).
What it means
PdoAdapter requires that any PDO instance passed to its constructor be configured to throw exceptions on error (PDO::ERRMODE_EXCEPTION). The adapter relies on exceptions to detect failed statements and report them; in the default silent mode PDO returns false instead, which would corrupt cache reads/writes silently. The constructor checks the attribute at src/Symfony/Component/Cache/Adapter/PdoAdapter.php:67-70 and rejects the connection if it is not set.
Source
Thrown at src/Symfony/Component/Cache/Adapter/PdoAdapter.php:69
* * db_connection_options: An array of driver-specific connection options [default: []]
*
* @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
* @throws InvalidArgumentException When namespace contains invalid characters
*/
public function __construct(#[\SensitiveParameter] \PDO|string $connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], ?MarshallerInterface $marshaller = null)
{
if (\is_string($connOrDsn) && str_contains($connOrDsn, '://')) {
throw new InvalidArgumentException(\sprintf('Usage of Doctrine DBAL URL with "%s" is not supported. Use a PDO DSN or "%s" instead.', __CLASS__, DoctrineDbalAdapter::class));
}
if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
throw new InvalidArgumentException(\sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
}
if ($connOrDsn instanceof \PDO) {
if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
throw new InvalidArgumentException(\sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__));
}
$this->conn = $connOrDsn;
} else {
$this->dsn = $connOrDsn;
}
$this->maxIdLength = self::MAX_KEY_LENGTH;
$this->table = $options['db_table'] ?? $this->table;
$this->idCol = $options['db_id_col'] ?? $this->idCol;
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
$this->username = $options['db_username'] ?? $this->username;
$this->password = $options['db_password'] ?? $this->password;
$this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions;
$this->namespace = $namespace;
$this->marshaller = $marshaller ?? new DefaultMarshaller();View on GitHub (pinned to 698e28026c)
Solutions
- Set `$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION)` before passing the PDO instance to the adapter.
- Pass a DSN string instead of a PDO instance and let PdoAdapter::createConnection configure the error mode for you.
- Use PdoAdapter::createConnection($dsn) which returns a properly configured PDO, then pass that.
Example fix
// before $pdo = new \PDO($dsn); $cache = new PdoAdapter($pdo, 'ns', 3600); // after $pdo = new \PDO($dsn); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $cache = new PdoAdapter($pdo, 'ns', 3600);
Defensive patterns
Strategy: validation
Validate before calling
// Run before constructing the adapter
if ($pdo instanceof \PDO && \PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
$cache = new PdoAdapter($pdo, $namespace, $lifetime); Try / catch
try {
$cache = new PdoAdapter($pdo, $namespace, $lifetime);
} catch (\Symfony\Component\Cache\Exception\InvalidArgumentException $e) {
// The PDO connection is misconfigured; set the attribute and retry,
// or fall back to passing a DSN string.
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$cache = new PdoAdapter($pdo, $namespace, $lifetime);
} Prevention
- Centralize PDO creation in a factory that always sets ERRMODE_EXCEPTION.
- Prefer passing a DSN string to PdoAdapter so it manages error mode itself.
- Document which connections are safe to share with the cache pool.
When it happens
Trigger: Constructing `new PdoAdapter($pdo, $namespace, $lifetime)` where `$pdo` is a pre-existing \PDO instance that was NOT configured with `$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION)`. Passing a DSN string does NOT trigger this (the adapter sets the mode itself via createConnection).
Common situations: Sharing a single PDO connection created elsewhere (Doctrine DBAL, a legacy bootstrap, or a manual `new \PDO($dsn)` with default ERRMODE_SILENT). Common when reusing the app's main DB connection for caching to save connections, or after upgrading the cache component which tightened this check.
Related errors
- Usage of Doctrine DBAL URL with "%s" is not supported. Use a
- Could not clear all cache pools, try specifying a specific p
- The "framework.cache.app" and "framework.cache.default_provi
- Cache namespace "%s" contains empty sub-namespace.
- Namespace must be %d chars max, %d given ("%s").
AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06).
Data as JSON: /api/errors/af5c6493f2099ce7.
Report an issue: GitHub.