getgrav/grav · error · InvalidArgumentException

Cache keys must be array or Traversable, "%s" given

Error message

Cache keys must be array or Traversable, "%s" given

What it means

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.

Source

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

     */
    public function clear(): bool
    {
        return $this->doClear();
    }

    /**
     * @param iterable $keys
     * @param mixed|null $default
     * @return iterable
     * @throws InvalidArgumentException
     */
    public function getMultiple(iterable $keys, mixed $default = null): iterable
    {
        if ($keys instanceof Traversable) {
            $keys = iterator_to_array($keys, false);
        } elseif (!is_array($keys)) {
            $isObject = is_object($keys);
            throw new InvalidArgumentException(
                sprintf(
                    'Cache keys must be array or Traversable, "%s" given',
                     $isObject ? $keys::class : gettype($keys)
                )
            );
        }

        if (empty($keys)) {
            return [];
        }

        $this->validateKeys($keys);
        $keys = array_unique($keys);
        $keys = array_combine($keys, $keys);

        $list = $this->doGetMultiple($keys, $this->miss);

        // Make sure that values are returned in the same order as the keys were given.

View on GitHub (pinned to 6040efed04)

Solutions

  1. Wrap single keys: use $cache->getMultiple(['foo']) instead of $cache->getMultiple('foo').
  2. Split strings first: $cache->getMultiple(explode(',', $rawKeys)).
  3. Assert the shape at the boundary: is_iterable($keys) before calling, and convert Traversables to arrays if you need to count them.

Example fix

// before
$values = $cache->getMultiple('menu,pages'); // string -> InvalidArgumentException

// after
$values = $cache->getMultiple(['menu', 'pages']);
Defensive patterns

Strategy: type-guard

Type guard

function normalizeKeys(mixed $keys): array
{
    if ($keys instanceof \Traversable) {
        return iterator_to_array($keys, false);
    }
    if (is_string($keys)) {
        return [$keys]; // tolerate single-key misuse
    }
    if (!is_array($keys)) {
        throw new \InvalidArgumentException('Cache keys must be iterable');
    }
    return $keys;
}

$values = $cache->getMultiple(normalizeKeys($keys));

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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