getgrav/grav · error · InvalidArgumentException

The class '%s' does not implement the '%s' interface

Error message

The class '%s' does not implement the '%s' interface

What it means

Every member of a ChainCache must implement Grav\Framework\Cache\CacheInterface (Grav's PSR-16-compatible contract). The constructor iterates the list and throws this InvalidArgumentException naming the offending class when a member does not. Mixing unrelated cache abstractions into the chain is the usual cause.

Source

Thrown at system/src/Grav/Framework/Cache/Adapter/ChainCache.php:52

     * @param array $caches
     * @param null|int|DateInterval $defaultLifetime
     * @throws InvalidArgumentException
     */
    public function __construct(array $caches, $defaultLifetime = null)
    {
        try {
            parent::__construct('', $defaultLifetime);
        } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
        }

        if (!$caches) {
            throw new InvalidArgumentException('At least one cache must be specified');
        }

        foreach ($caches as $cache) {
            if (!$cache instanceof CacheInterface) {
                throw new InvalidArgumentException(
                    sprintf(
                        "The class '%s' does not implement the '%s' interface",
                        $cache::class,
                        CacheInterface::class
                    )
                );
            }
        }

        $this->caches = array_values($caches);
        $this->count = count($caches);
    }

    /**
     * @inheritdoc
     */
    public function doGet($key, $miss)
    {

View on GitHub (pinned to 6040efed04)

Solutions

  1. Use Grav's built-in adapters, all of which implement the interface: FileCache, RedisCache, MemcachedCache, SessionCache, MemoryCache, DoctrineCache.
  2. Wrap foreign backends in an adapter class implementing Grav\Framework\Cache\CacheInterface (you can reuse CacheTrait) and put that adapter in the chain.
  3. For Doctrine specifically, use Grav's DoctrineCache adapter rather than passing the Doctrine instance raw.

Example fix

// before
$chain = new ChainCache([$redisClient, $psr6Pool]); // 'Redis' / 'Symfony\\Component\\Cache\\Adapter\\...' -> throws

// after
use Grav\Framework\Cache\Adapter\RedisCache;
$chain = new ChainCache([new RedisCache($redisClient)]);
Defensive patterns

Strategy: type-guard

Type guard

use Grav\Framework\Cache\CacheInterface;

/** @param CacheInterface[] $caches */
function assertChainAdapters(array $caches): void
{
    foreach ($caches as $c) {
        if (!$c instanceof CacheInterface) {
            throw new \InvalidArgumentException(get_class($c) . ' must be wrapped in a Grav cache adapter');
        }
    }
}

assertChainAdapters($caches);
$chain = new ChainCache($caches);

Prevention

When it happens

Trigger: Passing a PSR-6 CacheItemPoolInterface (Symfony/Doctrine cache pool), a Doctrine\Common\Cache instance, a Redis or Memcached client object, or a raw PSR-16 implementation from another vendor into `new ChainCache([...])` — none implement Grav's own CacheInterface.

Common situations: Porting code that used Doctrine Cache or Symfony Cache into a Grav extension; putting the Predis/Redis client where an adapter is expected; wrapping a third-party PSR-16 library assuming interface equality because the method names match.

Related errors


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