{"record":{"id":"3f6dbebcfedf6ac2","repo":"getgrav/grav","slug":"at-least-one-cache-must-be-specified","errorCode":null,"errorMessage":"At least one cache must be specified","messagePattern":"At least one cache must be specified","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Cache/Adapter/ChainCache.php","lineNumber":47,"sourceCode":"    /** @var int */\n    protected $count;\n\n    /**\n     * Chain Cache constructor.\n     * @param array $caches\n     * @param null|int|DateInterval $defaultLifetime\n     * @throws InvalidArgumentException\n     */\n    public function __construct(array $caches, $defaultLifetime = null)\n    {\n        try {\n            parent::__construct('', $defaultLifetime);\n        } catch (\\Psr\\SimpleCache\\InvalidArgumentException $e) {\n            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);\n        }\n\n        if (!$caches) {\n            throw new InvalidArgumentException('At least one cache must be specified');\n        }\n\n        foreach ($caches as $cache) {\n            if (!$cache instanceof CacheInterface) {\n                throw new InvalidArgumentException(\n                    sprintf(\n                        \"The class '%s' does not implement the '%s' interface\",\n                        $cache::class,\n                        CacheInterface::class\n                    )\n                );\n            }\n        }\n\n        $this->caches = array_values($caches);\n        $this->count = count($caches);\n    }\n","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Cache/Adapter/ChainCache.php#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n$adapters = array_filter([$redis ?? null, $memcached ?? null]); // empty when ext missing\n$cache = new ChainCache($adapters); // throws\n\n// after\n$adapters = $adapters ?: [new FileCache('cache://runtime')];\n$cache = new ChainCache($adapters);","handlingStrategy":"validation","validationCode":"$adapters = array_values(array_filter($candidateAdapters));\nif ($adapters === []) {\n    $adapters = [new \\Grav\\Framework\\Cache\\Adapter\\FileCache('cache://runtime')];\n}\n$chain = new \\Grav\\Framework\\Cache\\Adapter\\ChainCache($adapters);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["grav","cache","chain-cache","configuration"],"backgroundTag":"cache-adapter-misconfigured","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}