{"record":{"id":"5b207fb4986f4757","repo":"getgrav/grav","slug":"cache-keys-must-be-array-or-traversable-s-give","errorCode":null,"errorMessage":"Cache keys must be array or Traversable, \"%s\" given","messagePattern":"Cache keys must be array or Traversable, \"(.+?)\" given","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Cache/CacheTrait.php","lineNumber":144,"sourceCode":"     */\n    public function clear(): bool\n    {\n        return $this->doClear();\n    }\n\n    /**\n     * @param iterable $keys\n     * @param mixed|null $default\n     * @return iterable\n     * @throws InvalidArgumentException\n     */\n    public function getMultiple(iterable $keys, mixed $default = null): iterable\n    {\n        if ($keys instanceof Traversable) {\n            $keys = iterator_to_array($keys, false);\n        } elseif (!is_array($keys)) {\n            $isObject = is_object($keys);\n            throw new InvalidArgumentException(\n                sprintf(\n                    'Cache keys must be array or Traversable, \"%s\" given',\n                     $isObject ? $keys::class : gettype($keys)\n                )\n            );\n        }\n\n        if (empty($keys)) {\n            return [];\n        }\n\n        $this->validateKeys($keys);\n        $keys = array_unique($keys);\n        $keys = array_combine($keys, $keys);\n\n        $list = $this->doGetMultiple($keys, $this->miss);\n\n        // Make sure that values are returned in the same order as the keys were given.","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Cache/CacheTrait.php#L126-L162","documentation":"CacheTrait::getMultiple() (PSR-16 'get multiple') requires its keys argument to be an array or Traversable. If a value of any other type reaches the check, it throws InvalidArgumentException naming the actual type given. Note the method is natively typed `iterable`, so userland calls with a scalar usually fail earlier with a TypeError; this exception is the defensive path when the check runs inside untyped/internal call paths or older-PHP code paths.","triggerScenarios":"Calling $cache->getMultiple('key1,key2') with a comma-separated string instead of an array; passing a single key string 'foo' instead of ['foo']; passing an object that is neither Traversable nor array (e.g. ArrayAccess-only wrapper) via an internal untyped caller.","commonSituations":"Refactoring from get() to getMultiple() and forgetting to wrap the single key in array brackets; receiving keys from an API as a string and passing them unsplit; generics/iterable confusion when a method returns null and it is forwarded as-is.","solutions":["Wrap single keys: use $cache->getMultiple(['foo']) instead of $cache->getMultiple('foo').","Split strings first: $cache->getMultiple(explode(',', $rawKeys)).","Assert the shape at the boundary: is_iterable($keys) before calling, and convert Traversables to arrays if you need to count them."],"exampleFix":"// before\n$values = $cache->getMultiple('menu,pages'); // string -> InvalidArgumentException\n\n// after\n$values = $cache->getMultiple(['menu', 'pages']);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function normalizeKeys(mixed $keys): array\n{\n    if ($keys instanceof \\Traversable) {\n        return iterator_to_array($keys, false);\n    }\n    if (is_string($keys)) {\n        return [$keys]; // tolerate single-key misuse\n    }\n    if (!is_array($keys)) {\n        throw new \\InvalidArgumentException('Cache keys must be iterable');\n    }\n    return $keys;\n}\n\n$values = $cache->getMultiple(normalizeKeys($keys));","tryCatchPattern":null,"preventionTips":["Always pass array literals (['key1', 'key2']) to PSR-16 multi-ops.","is_iterable() guard at API boundaries that receive keys from external sources.","Split/join string formats explicitly instead of forwarding raw strings."],"tags":["grav","cache","psr-16","invalid-argument","iterable"],"backgroundTag":"psr16-invalid-argument","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}