serbanghita/Mobile-Detect · error · CacheInvalidArgumentException
Invalid key: '$key'. Must be alphanumeric, can contain _ and
Error message
Invalid key: '$key'. Must be alphanumeric, can contain _ and . and can be maximum of 64 chars.
What it means
A cache key was rejected by the strict policy in checkKey() (src/Cache/Cache.php:216): it must be non-empty, at most 64 characters, and match /^[A-Za-z0-9_.]{1,64}$/ — letters, digits, underscore and dot only. Hyphens, colons, spaces, slashes and UTF-8 characters are all forbidden, as is the empty string. This is an aggressive reading of the PSR-16 'valid characters' rule: invalid keys throw instead of being silently transformed.
Source
Thrown at src/Cache/Cache.php:216
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
/**
* @param mixed $key
* @throws CacheInvalidArgumentException
*/
protected function checkKey($key): string
{
if (!is_string($key)) {
throw new CacheInvalidArgumentException('Cache key must be a string.');
}
if ($key === '' || !preg_match('/^[A-Za-z0-9_.]{1,64}$/', $key)) {
throw new CacheInvalidArgumentException("Invalid key: '$key'. Must be alphanumeric, can contain _ and . and can be maximum of 64 chars.");
}
return $key;
}
/**
* @param mixed $ttl
* @throws CacheInvalidArgumentException
*/
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;
}
View on GitHub (pinned to 6ab7b0404d)
Solutions
- Hash or sanitize keys before use: $key = sha1($rawKey);, or preg_replace('/[^A-Za-z0-9_.]/', '_', $rawKey) then truncate to 64 chars.
- If you customize MobileDetect's cacheKeyFn, keep a hashing callback ('sha1', 'md5', or a hash() closure) — never a passthrough — because the internal composite key contains colons and raw User-Agent text.
- Reject or truncate keys over 64 characters at your own boundary so the cache never sees them.
Example fix
// before $detect = new MobileDetect(['cacheKeyFn' => fn($k) => $k]); $detect->isMobile(); // Invalid key: 'mobile:Mozilla/5.0 ...' (colons, >64 chars) // after $detect = new MobileDetect(); // default 'cacheKeyFn' => 'sha1'
Defensive patterns
Strategy: validation
Validate before calling
$safeKey = preg_replace('/[^A-Za-z0-9_.]/', '_', $rawKey);
if (strlen($safeKey) > 64) {
$safeKey = substr($safeKey, 0, 64);
}
$cache->get($safeKey); Type guard
function isValidCacheKey(string $key): bool
{
return preg_match('/^[A-Za-z0-9_.]{1,64}$/', $key) === 1;
} Try / catch
try {
$cache->get($key);
} catch (CacheInvalidArgumentException $e) {
if (str_contains($e->getMessage(), 'Invalid key')) {
$value = $cache->get(sha1($key)); // retry with a hashed, always-valid key
}
} Prevention
- Hash composite keys (sha1/md5) instead of embedding raw UA text or colons.
- If you change MobileDetect's cacheKeyFn, verify its output against the same [A-Za-z0-9_.]{1,64} rule.
- Add a unit test asserting your key generator passes the regex for your nastiest real User-Agent.
When it happens
Trigger: $cache->get('user-agent:iphone') (colon), $cache->set('is-mobile!', true) (exclamation), $cache->get('ua/iphone') (slash), $cache->get('') (empty), a key over 64 chars, or a custom 'cacheKeyFn' config that returns raw keys. Internally, createCacheKey() builds 'rule:userAgent:flatHeaders' and relies on the default sha1 callback to turn it into a 40-char hex string — replace that callback with a passthrough and this check fails on every detection call.
Common situations: Changing MobileDetect's 'cacheKeyFn' from 'sha1' to something returning raw or base64-encoded keys (base64_encode output contains +, /, = which are invalid); porting cache keys from another PSR-16 implementation that allowed the wider reserved set (A-Z a-z 0-9 _ . : / - ( ) ); copy-pasting URL fragments or UA substrings as keys.
Related errors
- Cache key must be a string.
- TTL must be null, int, or DateInterval.
- %s must be iterable.
- IS_MOBILE_ERR
- IS_TABLET_ERR
AI-assisted analysis of serbanghita/Mobile-Detect@6ab7b0404d (2026-08-21).
Data as JSON: /api/errors/c56997ccc5d135bf.
Report an issue: GitHub.