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

  1. Set `$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION)` before passing the PDO instance to the adapter.
  2. Pass a DSN string instead of a PDO instance and let PdoAdapter::createConnection configure the error mode for you.
  3. 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

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


AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06). Data as JSON: /api/errors/af5c6493f2099ce7. Report an issue: GitHub.