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

  1. Cast keys to string at call sites: $cache->get((string) $id) or $cache->get('page-' . $id).
  2. Build keys deliberately with a prefix + scalar interpolation so the result is always a string.
  3. 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

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


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