serbanghita/Mobile-Detect · error · CacheInvalidArgumentException
%s must be iterable.
Error message
%s must be iterable.
What it means
getMultiple(), setMultiple() or deleteMultiple() on the bundled Cache received a first argument that is not iterable (not an array and not Traversable). checkIterable() at src/Cache/Cache.php:243 formats the message with ucfirst($argName), so the thrown message actually reads 'Keys must be iterable.' or 'Values must be iterable.'. PSR-16 requires iterables for these batch methods; scalars, null, and non-Traversable objects fail this guard.
Source
Thrown at src/Cache/Cache.php:243
*/
protected function checkTtl($ttl): int|DateInterval|null
{
if ($ttl !== null && !is_int($ttl) && !($ttl instanceof DateInterval)) {
throw new CacheInvalidArgumentException('TTL must be null, int, or DateInterval.');
}
return $ttl;
}
/**
* @param mixed $iterable
* @return iterable<mixed>
* @throws CacheInvalidArgumentException
*/
protected function checkIterable($iterable, string $argName): iterable
{
if (!is_iterable($iterable)) {
throw new CacheInvalidArgumentException(sprintf('%s must be iterable.', ucfirst($argName)));
}
return $iterable;
}
protected function getTTL(DateInterval|int|null $ttl): ?int
{
if ($ttl instanceof DateInterval) {
return (new DateTime())->add($ttl)->getTimestamp() - time();
}
// We treat 0 as a valid value.
if (is_int($ttl)) {
return $ttl;
}
return null;
}View on GitHub (pinned to 6ab7b0404d)
Solutions
- Wrap single values in an array: $cache->getMultiple(['foo']) instead of $cache->getMultiple('foo').
- Decode JSON to arrays: json_decode($json, true).
- Guard at the boundary: if (!is_iterable($keys)) { throw new \InvalidArgumentException('keys must be iterable'); } with context about the origin.
Example fix
// before
$flags = $cache->getMultiple('isMobile');
// after
$flags = $cache->getMultiple(['isMobile', 'isTablet']); Defensive patterns
Strategy: validation
Validate before calling
if (!is_iterable($keys)) {
$keys = [$keys]; // or throw with context
}
$flags = $cache->getMultiple($keys); Type guard
function isKeyList(mixed $keys): bool
{
return is_iterable($keys)
&& (!is_array($keys) || array_all($keys, 'is_string'));
} Try / catch
try {
$cache->getMultiple($keys);
} catch (CacheInvalidArgumentException $e) {
// 'Keys must be iterable.' — normalize input and retry
$cache->getMultiple((array) $keys);
} Prevention
- Call *Multiple methods only with array literals: getMultiple(['a', 'b']).
- json_decode($json, true) so decoded structures are arrays, not stdClass.
- Re-check call sites when refactoring a method that feeds cache keys to return arrays.
When it happens
Trigger: $cache->getMultiple('foo') (single string instead of an array of keys), $cache->setMultiple(json_decode($json)) where the decode without true yields stdClass, $cache->deleteMultiple(null), or a refactor that changed a supplier method's return from array to scalar while callers still passed it to a *Multiple method.
Common situations: Confusing singular get/set/delete with their *Multiple counterparts; feeding decoded JSON objects directly; passing a single key 'for convenience'; generators already consumed (less common — those stay iterable but yield nothing).
Related errors
- Cache key must be a string.
- TTL must be null, int, or DateInterval.
- Invalid key: '$key'. Must be alphanumeric, can contain _ and
- IS_MOBILE_ERR
- IS_TABLET_ERR
AI-assisted analysis of serbanghita/Mobile-Detect@6ab7b0404d (2026-08-21).
Data as JSON: /api/errors/9d43f136b957274c.
Report an issue: GitHub.