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.
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)
Solutions
- Always pass at least one adapter: `new ChainCache([$primary])` or `[$fast, $slow]`.
- 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.
- 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
- Assert count($caches) >= 1 before constructing ChainCache.
- When adapters are conditionally included (extension availability), always keep one unconditional fallback adapter.
- Cover the empty-list case in unit tests for cache factory code.
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
- Cache folder not defined.
- The class '%s' does not implement the '%s' interface
- No backups defined...
- Backup location: {$backup_root} does not exist...
- Setup: Configuration reload loop detected!
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/3f6dbebcfedf6ac2.
Report an issue: GitHub.