{"record":{"id":"f631d8a7a12ab4ac","repo":"getgrav/grav","slug":"cache-key-length-must-be-less-than-65-characters","errorCode":null,"errorMessage":"Cache key length must be less than 65 characters, key had %d characters","messagePattern":"Cache key length must be less than 65 characters, key had (.+?) characters","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Cache/CacheTrait.php","lineNumber":317,"sourceCode":"     * @param string|mixed $key\n     * @return void\n     * @throws InvalidArgumentException\n     */\n    protected function validateKey(mixed $key): void\n    {\n        if (!is_string($key)) {\n            throw new InvalidArgumentException(\n                sprintf(\n                    'Cache key must be string, \"%s\" given',\n                    get_debug_type($key)\n                )\n            );\n        }\n        if (!isset($key[0])) {\n            throw new InvalidArgumentException('Cache key length must be greater than zero');\n        }\n        if (strlen($key) > 64) {\n            throw new InvalidArgumentException(\n                sprintf('Cache key length must be less than 65 characters, key had %d characters', strlen($key))\n            );\n        }\n        if (strpbrk($key, '{}()/\\@:') !== false) {\n            throw new InvalidArgumentException(\n                sprintf('Cache key \"%s\" contains reserved characters {}()/\\@:', $key)\n            );\n        }\n    }\n\n    /**\n     * @param array $keys\n     * @return void\n     * @throws InvalidArgumentException\n     */\n    protected function validateKeys(iterable $keys): void\n    {\n        if (!$this->validation) {","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Cache/CacheTrait.php#L299-L335","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","solutions":["Hash long keys: $key = md5($rawKey) (optionally keep a short prefix: 'pg-' . md5($url)) — deterministic and always 32/40 chars.","Extract a compact natural key first (page slug + language) and only hash when it overflows.","Centralize key building in one helper that enforces the 64-char budget so every call site inherits it."],"exampleFix":"// before\n$cache->get('fragment-' . $uri . '?' . http_build_query($query)); // >64 chars -> throws\n\n// after\n$cache->get('fragment-' . md5($uri . '?' . http_build_query($query))); // 8+32 chars","handlingStrategy":"validation","validationCode":"if (strlen($key) > 64) {\n    $key = 'h-' . md5($key); // 34 chars, deterministic\n}\n$cache->get($key);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Hash URL/path-derived keys with md5() unconditionally — simplest way to stay under budget.","Keep a short readable prefix plus hash suffix ('pg-' . md5($uri)) for debuggability.","Add a key-length assertion to your key-builder helper's tests."],"tags":["grav","cache","psr-16","invalid-key","key-length"],"backgroundTag":"invalid-cache-key","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}