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
- Use Grav's built-in adapters, all of which implement the interface: FileCache, RedisCache, MemcachedCache, SessionCache, MemoryCache, DoctrineCache.
- Wrap foreign backends in an adapter class implementing Grav\Framework\Cache\CacheInterface (you can reuse CacheTrait) and put that adapter in the chain.
- 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
- Only put Grav\Framework\Cache adapters (FileCache, RedisCache, MemcachedCache, ...) into a ChainCache.
- Never pass raw clients (Redis, Predis, Memcached) or PSR-6 pools — wrap them first.
- Let your IDE/phpstan enforce a `CacheInterface[]` docblock type on the constructor argument.
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
- At least one cache must be specified
- Cache keys must be array or Traversable, "%s" given
- Cache values must be array or Traversable, "%s" given
- Cache key must be string, "%s" given
- Cache key length must be greater than zero
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/38e91bd7775f27c4.
Report an issue: GitHub.