getgrav/grav · error · InvalidArgumentException
Cache values must be array or Traversable, "%s" given
Error message
Cache values must be array or Traversable, "%s" given
What it means
CacheTrait::setMultiple() (PSR-16 'set multiple') requires its values argument to be an array or Traversable; anything else triggers InvalidArgumentException('Cache values must be array or Traversable, ...'). As with the other multi-ops the parameter is natively typed iterable, so the exception guards internal/legacy call paths while typical userland misuse surfaces as TypeError first.
Source
Thrown at system/src/Grav/Framework/Cache/CacheTrait.php:187
}
}
return $values;
}
/**
* @param iterable $values
* @param null|int|DateInterval $ttl
* @return bool
* @throws InvalidArgumentException
*/
public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
{
if ($values instanceof Traversable) {
$values = iterator_to_array($values, true);
} elseif (!is_array($values)) {
$isObject = is_object($values);
throw new InvalidArgumentException(
sprintf(
'Cache values must be array or Traversable, "%s" given',
$isObject ? $values::class : gettype($values)
)
);
}
$keys = array_keys($values);
if (empty($keys)) {
return true;
}
$this->validateKeys($keys);
$ttl = $this->convertTtl($ttl);
// If a negative or zero TTL is provided, the item MUST be deleted from the cache.View on GitHub (pinned to 6040efed04)
Solutions
- Always pass a key=>value map: $cache->setMultiple(['menu' => $menu, 'pages' => $pages], $ttl).
- Use json_decode($json, true) so decoded objects become arrays, which pass the check.
- For single values, use set($key, $value) instead of setMultiple().
Example fix
// before $cache->setMultiple(json_decode($payload)); // stdClass -> InvalidArgumentException // after $cache->setMultiple((array)json_decode($payload, true));
Defensive patterns
Strategy: type-guard
Type guard
function normalizeValues(mixed $values): array
{
if ($values instanceof \Traversable) {
return iterator_to_array($values, true);
}
if ($values instanceof \stdClass) {
return (array) $values; // json_decode without assoc=true
}
return is_array($values) ? $values : throw new \InvalidArgumentException('values must be iterable');
}
$cache->setMultiple(normalizeValues($values), $ttl); Prevention
- Use json_decode($json, true) when decoded data feeds setMultiple().
- Keep values in key=>value map form; use set() for single items.
- Guard generator-returning helpers: a null/false early return must not reach setMultiple().
When it happens
Trigger: Calling $cache->setMultiple('serialized-string-of-values'); passing a generator-returning false/early-return value (null) straight into setMultiple(); passing an ArrayAccess object that is not Traversable; forwarding a decoded JSON scalar (string/number) instead of the expected object-as-array.
Common situations: json_decode($json) without the second argument returning an object (stdClass is Traversable? no — stdClass is NOT Traversable, so decoding a JSON object without `true` and passing it raises this); caching a single value and forgetting key=>value pairing; API responses that are scalars rather than maps.
Related errors
- Cache keys must be array or Traversable, "%s" given
- The class '%s' does not implement the '%s' interface
- Cache key must be string, "%s" given
- Cache key length must be greater than zero
- Cache key length must be less than 65 characters, key had %d
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/3607e05e5909d581.
Report an issue: GitHub.