doctrine/cache · error · InvalidArgument

Cache key " " contains reserved characters " ".

Error message

Cache key "%s" contains reserved characters "%s".

What it means

PSR-6 reserves the characters {}()/\@: in cache keys, and Doctrine's CacheAdapter enforces this via strpbrk against self::RESERVED_CHARACTERS. validKey() throws InvalidArgument when a key passed to getItem/hasItem/deleteItem/deleteItems/validKeys contains any of them.

Solutions

  1. Sanitize the key: replace reserved characters, e.g. preg_replace('/[{}()\/\\@:]/', '_', $key)
  2. Hash the offending value, e.g. md5() or sha1() of the raw identifier, to guarantee a safe key
  3. Prefix with a safe namespace and keep only safe parts of the raw value in the key

Example fix

// before
$item = $pool->getItem($email); // 'user@example.com'
// after
$item = $pool->getItem('email_' . md5($email));
Defensive patterns

Strategy: validation

Validate before calling

if (strpbrk($key, '{}()/\\@:') !== false) { $key = preg_replace('/[{}()\/\\@:]/', '_', $key); }

Type guard

function hasNoReservedChars(string $key): bool { return strpbrk($key, '{}()/\\@:') === false; }

Try / catch

try { $item = $pool->getItem($key); } catch (\Doctrine\Common\Cache\Psr6\InvalidArgument $e) { $item = $pool->getItem(md5($rawKey)); }

Prevention

When it happens

Trigger: Keys derived from file paths (/), emails (@), URLs (: /), class names with namespaces (\), or templates ({}) passed directly to the pool.

Common situations: Caching by email address or file path; using FQCN as a key; concatenating a URI segment containing ':'; keys built from user-supplied URLs.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of doctrine/cache@e0a9919443 (2026-09-13). Data as JSON: /api/errors/fe8884b1b9456042. Report an issue: GitHub.

Appendix: source

Thrown at lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php:265

    {
        $this->commit();
    }

    /**
     * @param mixed $key
     */
    private static function validKey($key): bool
    {
        if (! is_string($key)) {
            throw new InvalidArgument(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
        }

        if ($key === '') {
            throw new InvalidArgument('Cache key length must be greater than zero.');
        }

        if (strpbrk($key, self::RESERVED_CHARACTERS) !== false) {
            throw new InvalidArgument(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS));
        }

        return true;
    }

    /**
     * @param mixed[] $keys
     */
    private static function validKeys(array $keys): bool
    {
        foreach ($keys as $key) {
            self::validKey($key);
        }

        return true;
    }

    /**

View on GitHub (pinned to e0a9919443)