{"record":{"id":"bfed45614d7747da","repo":"doctrine/cache","slug":"cache-key-must-be-string-s-given","errorCode":null,"errorMessage":"Cache key must be string, \"%s\" given.","messagePattern":"Cache key must be string, \"(.+?)\" given\\.","errorType":"exception","errorClass":"InvalidArgument","httpStatus":null,"severity":"error","filePath":"lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php","lineNumber":257,"sourceCode":"        foreach ($byLifetime as $lifetime => $values) {\n            $success = $this->doSaveMultiple($values, $lifetime) && $success;\n        }\n\n        return $success;\n    }\n\n    public function __destruct()\n    {\n        $this->commit();\n    }\n\n    /**\n     * @param mixed $key\n     */\n    private static function validKey($key): bool\n    {\n        if (! is_string($key)) {\n            throw new InvalidArgument(sprintf('Cache key must be string, \"%s\" given.', is_object($key) ? get_class($key) : gettype($key)));\n        }\n\n        if ($key === '') {\n            throw new InvalidArgument('Cache key length must be greater than zero.');\n        }\n\n        if (strpbrk($key, self::RESERVED_CHARACTERS) !== false) {\n            throw new InvalidArgument(sprintf('Cache key \"%s\" contains reserved characters \"%s\".', $key, self::RESERVED_CHARACTERS));\n        }\n\n        return true;\n    }\n\n    /**\n     * @param mixed[] $keys\n     */\n    private static function validKeys(array $keys): bool\n    {","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/doctrine/cache/blob/e0a9919443c1e918251c23b65eddd9a66fafacb3/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php#L239-L275","documentation":"Doctrine's PSR-6 CacheAdapter requires cache keys to be non-empty strings without reserved characters. validKey() throws this InvalidArgument when getItem, hasItem, deleteItem, deleteItems, or validKeys receives a key that is not a string. The message reports the PHP type (or class name) of the value actually passed.","triggerScenarios":"Calling $pool->getItem($key), hasItem, deleteItem or deleteItems with a null, int, object (e.g. a key object or entity), float, or array instead of a string.","commonSituations":"Passing a user-supplied ID that is an integer from a database row; forgetting to cast/serialize a key; passing null from an unconfigured lookup; refactors where the key variable became an object.","solutions":["Cast or build the key as a string before calling the pool, e.g. (string) $id or 'user_' . $id","If the key is an object, derive a string from it (e.g. ->getId() or serialize/md5 of a stable representation)","Guard with is_string($key) before calling the adapter and handle invalid input at the boundary","Check callers for null values from config/databases that flow into the key position"],"exampleFix":"// before\n$item = $pool->getItem($row['id']); // int\n// after\n$item = $pool->getItem((string) $row['id']);","handlingStrategy":"type-guard","validationCode":"if (!is_string($key) || $key === '') { throw new InvalidArgumentException('Invalid cache key'); }","typeGuard":"function isStringKey(mixed $key): bool { return is_string($key) && $key !== ''; }","tryCatchPattern":"try { $item = $pool->getItem($key); } catch (\\Doctrine\\Common\\Cache\\Psr6\\InvalidArgument $e) { /* log and skip cache */ }","preventionTips":["Always build cache keys via a single helper that enforces string keys","Cast database IDs to string at the boundary","Never pass raw user/config values straight into pool methods"],"tags":["php","psr6","cache","type-error"],"backgroundTag":"invalid-argument","analyzedSha":"e0a9919443c1e918251c23b65eddd9a66fafacb3","analyzedAt":"2026-09-13T19:23:46.714Z","contentChangedAt":"2026-09-13T19:23:46.714Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}