Maximum call stack size reached. Infinite recursion?

Created on 14 July 2024, about 2 months ago
Updated 15 July 2024, about 2 months ago

Problem/Motivation

After updating to Drupal 10.3, I get the following error when trying to visit any page on my site:

Error: Maximum call stack size of 8355840 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? in Drupal\Component\DependencyInjection\Container->get() (line 129 of core/lib/Drupal/Component/DependencyInjection/Container.php).

Drupal\Component\DependencyInjection\Container->get('cache_context.user.is_super_user') (Line: 222)
Drupal\Core\Cache\Context\CacheContextsManager->getService('user.is_super_user') (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 242)
Drupal\Core\Cache\VariationCache->createCacheIdFast(Array, Object) (Line: 173)
Drupal\Core\Cache\VariationCache->getRedirectChain(Array, Object) (Line: 35)
Drupal\Core\Cache\VariationCache->get(Array, Object) (Line: 80)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)

The following part of the call stack is being repeated indefinitely:

Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)

Steps to reproduce

Might be a combination of this core version with certain custom/contrib modules or config, but I haven't been able to figure that out.

Proposed resolution

I was able to come up with a workaround: in AccessPolicyProcessor::processAccessPolicies(), if the resulting CacheableMetadata contains the 'user.permissions' cache context, remove it. This cache key invokes PermissionsHashGenerator::getCacheableMetadata(), which in turn invokes AccessPolicyProcessor::processAccessPolicies(), creating an infinite loop.

Remaining tasks

Find a better fix if necessary.

User interface changes

API changes

Data model changes

Release notes snippet

πŸ› Bug report
Status

Needs work

Version

11.0 πŸ”₯

Component
BaseΒ  β†’

Last updated 1 minute ago

Created by

πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels

Live updates comments and jobs are added and updated live.
  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @DieterHolvoet
  • Status changed to Needs review about 2 months ago
  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels
  • Pipeline finished with Success
    about 2 months ago
    Total: 581s
    #223808
  • Pipeline finished with Success
    about 2 months ago
    Total: 548s
    #223821
  • πŸ‡ΊπŸ‡ΈUnited States cilefen

    Is xdebug enabled? I remember there were issues with that, although this of course may be unrelated.

  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels

    Yes, Xdebug is enabled.

  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels

    Oh and might be worth mentioning, the user.permissions cache context is attached to the role entity, which at some point is added as cacheable dependency.

  • Status changed to Needs work about 2 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    MR should be updated for 11.x

    Nothing major stuck out but definitely seems like something that will need test coverage

  • Pipeline finished with Canceled
    about 2 months ago
    Total: 103s
    #224762
  • Pipeline finished with Canceled
    about 2 months ago
    Total: 105s
    #224763
  • Pipeline finished with Canceled
    about 2 months ago
    Total: 66s
    #224765
  • Pipeline finished with Success
    about 2 months ago
    Total: 482s
    #224768
  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    @Dieter
    I debugged a similar thing lately, it looks like the new permission negitiation code is not robust against some config override cacheability.
    Can you grep "implements ConfigFactoryOverrideInterface" modules?

  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels

    That's it! The following class in my custom code seems to be the culprit. I don't think I'm doing anything wrong there though?

    <?php
    
    namespace Drupal\my_module\Config;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Config\ConfigFactoryOverrideInterface;
    use Drupal\Core\Config\StorageInterface;
    use Drupal\Core\Session\AccountProxyInterface;
    
    class MenuBreadcrumbConfigOverrides implements ConfigFactoryOverrideInterface
    {
        public function __construct(
            protected AccountProxyInterface $currentUser,
        ) {
        }
    
        public function loadOverrides($names): array
        {
            $overrides = [];
    
            if (!in_array('menu_breadcrumb.settings', $names, true)) {
                return $overrides;
            }
    
            if ($this->currentUser->hasPermission('access toolbar')) {
                if ($this->currentUser->hasPermission('access editor toolbar')) {
                    $overrides['menu_breadcrumb.settings']['menu_breadcrumb_menus']['editor']['enabled'] = 1;
                } else {
                    $overrides['menu_breadcrumb.settings']['menu_breadcrumb_menus']['admin']['enabled'] = 1;
                }
            }
    
            return $overrides;
        }
    
        public function getCacheSuffix(): string
        {
            return 'MenuBreadcrumbConfigOverrides';
        }
    
        public function getCacheableMetadata($name): CacheableMetadata
        {
            return (new CacheableMetadata())
                ->addCacheContexts(['user.permissions']);
        }
    
        public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION)
        {
            return null;
        }
    }
    
  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    Yo! I made the same omission in translation_bliss module, now fixed:
    You need a $name check in getCacheableMetadata.

    That said, imho it's still a core bug if it WSODs on too much config cachability.

  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels

    You're right, that fixes it for me. Thanks!

Production build 0.71.5 2024