#41274·magento2

A cache type declared in `cache.xml` but absent from the deployment configuration is silently treated as disabled, with no way to tell it apart from a type that was deliberately turned off

Author: lbajsarowiczCreated Sep 17, 2026Updated Sep 18, 2026
Labelsfeature requestTriage: Dev.ExperienceProgress: ready for groomingReported on 2.4.x

Summary

Magento\Framework\App\Cache\State::isEnabled() resolves an unknown cache type to false:

php
public function isEnabled($cacheType): bool
{
    $this->load();
    return (bool)($this->statuses[$cacheType] ?? false);
}

That default is deliberate and I am not asking for it to change: a cache type that a module has just introduced must not switch itself on behind the operator's back, and defaulting to off is the safe side of that trade. The problem is that the state "this type is off because nobody ever configured it" and the state "this type is off because somebody turned it off" are rendered identically everywhere the platform reports cache state, so an installation can lose a cache type — including full_page — and there is no signal anywhere that it happened.

In practice the map in app/etc/env.php or app/etc/config.php and the set of types declared by the enabled modules drift apart for ordinary reasons: a module is added by a composer update and its cache.xml type never reaches the deployment configuration of an environment that was provisioned from a stored env.php; a deployment pipeline templates the cache_types section from a fixed list; env.php is restored from a snapshot taken before a module was installed. The cost is a cache that never caches, on an environment where everything reports success.

Tested on vanilla 2.4-develop at commit 12d212031006ec455fee51c1653a6ac230aeca54, PHP 8.4.25, MariaDB 11.4, plain setup:install, no third-party modules. This installation declares 15 cache types.

Examples

Remove two declared types from the deployment configuration, leaving 13 of the 15:

php -r '
    $c = include "app/etc/config.php";
    unset($c["cache_types"]["full_page"], $c["cache_types"]["block_html"]);
    file_put_contents("app/etc/config.php", "<?php\nreturn " . var_export($c, true) . ";\n");
'

bin/magento cache:status reports them as if an operator had disabled them:

Current status:
                          config: 1
                          layout: 1
                      block_html: 0
                     collections: 1
                      reflection: 1
                          db_ddl: 1
                 compiled_config: 1
                             eav: 1
           customer_notification: 1
   graphql_query_resolver_result: 1
              config_integration: 1
          config_integration_api: 1
                       full_page: 0
               config_webservice: 1
                       translate: 1

A probe reading DeploymentConfig, Cache\State and Cache\TypeListInterface directly shows the difference that the CLI does not:

PROBE_MERGED_COUNT=13
PROBE_CONFIG_PHP=count=13
PROBE_ENV_PHP=ABSENT
PROBE_DECLARED_COUNT=15
PROBE_TYPE block_html    declared=Y merged=- isEnabled=0 cfgphp=- envphp=-
PROBE_TYPE full_page     declared=Y merged=- isEnabled=0 cfgphp=- envphp=-
PROBE_TYPE collections   declared=Y merged=1 isEnabled=1 cfgphp=1 envphp=-

merged=- means the key is absent from the configuration entirely, merged=0 would mean it is present and set to off. cache:status prints 0 for both.

bin/magento setup:upgrade --keep-generated in that state exits 0 and says nothing about the two types:

CMD: bin/magento setup:upgrade --keep-generated
EXIT: 0
[SUCCESS]: Cache types config flushed successfully
[SUCCESS]: Cache cleared successfully
Updating modules:
Schema creation/updates:
...
Upgrade completed successfully.

Nothing is written to the log, no non-zero exit code, and cache:flush and cache:clean are equally silent. The only way an operator finds out is by knowing in advance how many types the enabled modules declare and comparing that number against cache:status by hand.

Proposed solution

Either of these would be enough, and the first is the smaller change:

Make cache:status distinguish the two states, for example by listing types that are declared by an enabled module but absent from the deployment configuration under a separate heading, or by printing a third value instead of 0 for them. The data needed is already available at that point: Magento\Framework\App\Cache\TypeListInterface::getTypes() gives the declared set and DeploymentConfig::getConfigData('cache_types') gives the configured set, so this is a presentation change in Magento\Backend\Console\Command\CacheStatusCommand rather than a behaviour change.

Or have setup:upgrade emit a warning naming the types that enabled modules declare and that are missing from the configuration, in the same place it already reports module and schema changes. setup:upgrade is the point where new modules become active, which is exactly when the drift is introduced, and a line there reaches the deployment pipeline logs of every installation rather than only the operator who thought to run cache:status.

In both cases the runtime default stays false and nothing turns itself on.

Release note

bin/magento cache:status now distinguishes cache types that are not present in the deployment configuration from cache types that have been explicitly disabled.

Triage and priority

  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.