getgrav/grav · error · InvalidArgumentException

Cache key length must be less than 65 characters, key had %d

Error message

Cache key length must be less than 65 characters, key had %d characters

What it means

Grav's cache key validator enforces the PSR-16 maximum key length of 64 characters. Keys longer than 64 chars throw InvalidArgumentException reporting the actual length, because many backends (memcached, some Redis setups, filesystem adapters) misbehave or truncate long keys.

Source

Thrown at system/src/Grav/Framework/Cache/CacheTrait.php:317

     * @param string|mixed $key
     * @return void
     * @throws InvalidArgumentException
     */
    protected function validateKey(mixed $key): void
    {
        if (!is_string($key)) {
            throw new InvalidArgumentException(
                sprintf(
                    'Cache key must be string, "%s" given',
                    get_debug_type($key)
                )
            );
        }
        if (!isset($key[0])) {
            throw new InvalidArgumentException('Cache key length must be greater than zero');
        }
        if (strlen($key) > 64) {
            throw new InvalidArgumentException(
                sprintf('Cache key length must be less than 65 characters, key had %d characters', strlen($key))
            );
        }
        if (strpbrk($key, '{}()/\@:') !== false) {
            throw new InvalidArgumentException(
                sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key)
            );
        }
    }

    /**
     * @param array $keys
     * @return void
     * @throws InvalidArgumentException
     */
    protected function validateKeys(iterable $keys): void
    {
        if (!$this->validation) {

View on GitHub (pinned to 6040efed04)

Solutions

  1. Hash long keys: $key = md5($rawKey) (optionally keep a short prefix: 'pg-' . md5($url)) — deterministic and always 32/40 chars.
  2. Extract a compact natural key first (page slug + language) and only hash when it overflows.
  3. Centralize key building in one helper that enforces the 64-char budget so every call site inherits it.

Example fix

// before
$cache->get('fragment-' . $uri . '?' . http_build_query($query)); // >64 chars -> throws

// after
$cache->get('fragment-' . md5($uri . '?' . http_build_query($query))); // 8+32 chars
Defensive patterns

Strategy: validation

Validate before calling

if (strlen($key) > 64) {
    $key = 'h-' . md5($key); // 34 chars, deterministic
}
$cache->get($key);

Prevention

When it happens

Trigger: Using full page URLs, long serialized parameter lists, or concatenated entity identifiers as raw keys (e.g. 'page-/very/long/nested/path/with?many=query&params=...'); composite keys built by joining every context variable; getMultiple with oversized members hitting validateKeys().

Common situations: Caching rendered fragments keyed by raw URI on content-heavy sites; multi-language sites prepending long locale+route prefixes; query-string-heavy listing pages producing unique but very long keys.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/f631d8a7a12ab4ac. Report an issue: GitHub.