getgrav/grav · error · InvalidArgumentException
Cache key must be string, "%s" given
Error message
Cache key must be string, "%s" given
What it means
The shared PSR-16 key validator in CacheTrait (used by every Grav cache adapter's get/set/delete/has) requires keys to be strings. Passing an int, float, bool, null, or object throws InvalidArgumentException with the debug type of the value. This is stricter than PHP coercion: Grav validates explicitly so that key semantics are deterministic across backends.
Source
Thrown at system/src/Grav/Framework/Cache/CacheTrait.php:306
{
$success = true;
foreach ($keys as $key) {
$success = $this->doDelete($key) && $success;
}
return $success;
}
/**
* @param string|mixed $key
* @return void
* @throws InvalidArgumentException
*/
protected function validateKey(mixed $key): void
{
if (!is_string($key)) {
throw new InvalidArgumentException(
sprintf(
'Cache key must be string, "%s" given',
get_debug_type($key)
)
);
}
if (!isset($key[0])) {
throw new InvalidArgumentException('Cache key length must be greater than zero');
}
if (strlen($key) > 64) {
throw new InvalidArgumentException(
sprintf('Cache key length must be less than 65 characters, key had %d characters', strlen($key))
);
}
if (strpbrk($key, '{}()/\@:') !== false) {
throw new InvalidArgumentException(
sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key)
);View on GitHub (pinned to 6040efed04)
Solutions
- Cast keys to string at call sites: $cache->get((string) $id) or $cache->get('page-' . $id).
- Build keys deliberately with a prefix + scalar interpolation so the result is always a string.
- Before multi-ops, sanitize: $keys = array_map('strval', $keys); to avoid validateKeys() routing each bad key into this error.
Example fix
// before
$cache->get($page->id()); // int -> 'Cache key must be string, "int" given'
// after
$cache->get('page-' . $page->id()); // interpolated -> always string Defensive patterns
Strategy: type-guard
Type guard
function isValidCacheKey(mixed $key): bool
{
return is_string($key) && $key !== '' && strlen($key) <= 64
&& strpbrk($key, '{}()/\\@:') === false;
}
// usage
$cache->get(isValidCacheKey($key) ? $key : 'page-' . md5((string) $key)); Prevention
- Interpolate IDs into key strings: 'user-' . $id, never $id alone.
- array_map('strval', $keys) before multi-operations.
- Run a single key-builder helper with a string return type through your codebase.
When it happens
Trigger: Calling $cache->get(42) with a numeric database ID; passing null because a lookup variable failed to resolve; passing an int-keyed value from a foreach over an array with numeric keys; calling getMultiple/deleteMultiple with ['a', 0 => 'b'] where validateKeys() forwards non-string keys here.
Common situations: Using entity/page IDs directly as cache keys; array_map/foreach producing mixed key types; database rows whose IDs come back as ints (PDO default) being reused as keys; passing false from a failed strpos check as a key.
Related errors
- Cache key length must be greater than zero
- Cache key length must be less than 65 characters, key had %d
- Cache key "%s" contains reserved characters {}()/\@:
- Expiration date must be an integer, a DateInterval or null,
- The class '%s' does not implement the '%s' interface
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/f6af558f6121d918.
Report an issue: GitHub.