getgrav/grav · error · InvalidArgumentException

At least one cache must be specified

Error message

At least one cache must be specified

What it means

ChainCache composes several cache backends into one read-through chain and mandates a non-empty list: its constructor throws InvalidArgumentException('At least one cache must be specified') when the $caches array is empty. An empty chain has no semantics (nothing to read from or write to), so Grav refuses to construct it.

Solutions

  1. Always pass at least one adapter: `new ChainCache([$primary])` or `[$fast, $slow]`.
  2. If the chain is config-driven, fall back to a always-available adapter (e.g. Grav\Framework\Cache\Adapter\FileCache) when the filtered list ends up empty.
  3. Fix the environment so the intended adapters survive filtering: install php-redis/php-memcached or correct the driver names in cache configuration.

Example fix

// before
$adapters = array_filter([$redis ?? null, $memcached ?? null]); // empty when ext missing
$cache = new ChainCache($adapters); // throws

// after
$adapters = $adapters ?: [new FileCache('cache://runtime')];
$cache = new ChainCache($adapters);
Defensive patterns

Strategy: validation

Validate before calling

$adapters = array_values(array_filter($candidateAdapters));
if ($adapters === []) {
    $adapters = [new \Grav\Framework\Cache\Adapter\FileCache('cache://runtime')];
}
$chain = new \Grav\Framework\Cache\Adapter\ChainCache($adapters);

Prevention

When it happens

Trigger: Constructing `new ChainCache([])` directly; building the chain from configuration where every configured adapter was filtered out (e.g. driver availability check removed Redis/Memcached because the extension is missing) leaving zero entries; dynamic code assembling adapters based on runtime conditions that all evaluate false.

Common situations: A cache config listing only non-PHP-extension drivers (redis, memcached) on a host lacking those extensions, so the chain ends up empty; conditional adapter registration (staging vs production) with a typo making no branch match; test fixtures creating ChainCache from an empty provider list.

Related errors


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

Appendix: source

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

    /** @var int */
    protected $count;

    /**
     * Chain Cache constructor.
     * @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);
    }

View on GitHub (pinned to 6040efed04)