symfony/http-foundation · error · InvalidArgumentException

" " requires PDO error mode attribute be set to throw…

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

PdoSessionHandler relies on PDO throwing exceptions so SQL errors during session read/write surface as exceptions rather than silent failures. When a \PDO instance is passed with ATTR_ERRMODE set to anything other than ERRMODE_EXCEPTION, the constructor throws an InvalidArgumentException.

Solutions

  1. Call $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION) before passing the PDO to PdoSessionHandler.
  2. Configure the PDO connection factory used by the app to always set ERRMODE_EXCEPTION.
  3. Alternatively pass a DSN string (or URL) plus options instead of a pre-built PDO instance, letting PdoSessionHandler configure error mode itself.

Example fix

// before
$pdo = new \PDO($dsn, $user, $pass);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$handler = new PdoSessionHandler($pdo); // throws

// after
$pdo = new \PDO($dsn, $user, $pass);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$handler = new PdoSessionHandler($pdo);
Defensive patterns

Strategy: validation

Validate before calling

if ($pdo instanceof \PDO && \PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
    $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
$handler = new PdoSessionHandler($pdo);

Try / catch

try {
    $handler = new PdoSessionHandler($pdo);
} catch (\InvalidArgumentException $e) {
    // set ERRMODE_EXCEPTION on the PDO and retry construction
}

Prevention

When it happens

Trigger: Passing an existing \PDO object whose error mode is ERRMODE_SILENT or ERRMODE_WARNING to new PdoSessionHandler($pdo).

Common situations: Reusing an application-wide PDO connection configured with legacy error modes; legacy codebases that set ERRMODE_SILENT globally; framework bootstrapping that resets PDO attributes after construction.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of symfony/http-foundation@5aea19cd67 (2026-09-13). Data as JSON: /api/errors/906b0698a16ecea1. Report an issue: GitHub.

Appendix: source

Thrown at Session/Storage/Handler/PdoSessionHandler.php:164

     *  * db_id_col: The column where to store the session id [default: sess_id]
     *  * db_data_col: The column where to store the session data [default: sess_data]
     *  * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime]
     *  * db_time_col: The column where to store the timestamp [default: sess_time]
     *  * db_username: The username when lazy-connect [default: '']
     *  * db_password: The password when lazy-connect [default: '']
     *  * db_connection_options: An array of driver-specific connection options [default: []]
     *  * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
     *  * ttl: The time to live in seconds.
     *
     * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
     *
     * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
     */
    public function __construct(#[\SensitiveParameter] \PDO|string|null $pdoOrDsn = null, #[\SensitiveParameter] array $options = [])
    {
        if ($pdoOrDsn instanceof \PDO) {
            if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->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->pdo = $pdoOrDsn;
            $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
        } elseif (\is_string($pdoOrDsn) && str_contains($pdoOrDsn, '://')) {
            $this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
        } else {
            $this->dsn = $pdoOrDsn;
        }

        $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;

View on GitHub (pinned to 5aea19cd67)