Problem/Motivation
Every page of a site white screens when domain_config_ui and core's Interface Translation (locale) module are both installed. Introduced in 3.1.0-alpha5 by #3618091: Allow a config key to stay overridden when its value equals the base configuration.
That issue gave domain.config_factory_override a module_handler constructor argument, so it could invoke hook_domain_config_pinned_keys_alter(). The service is tagged config.factory.override, and domain_config_ui.factory decorates config.factory and collects those overrides, so the argument closes a loop back onto the service being built.
The container reports it as:
Circular reference detected for service "domain_config_ui.factory", path: options_request_listener, router.route_provider, path_processor_manager, domain.prefix_path_processor, domain.resolver, entity_type.manager, string_translation, string_translator.locale.lookup, domain_config_ui.factory, domain.config_factory_override
The primary exception is an ArgumentCountError; the circular reference is the error page failing to render on top of it, which is why nothing useful reaches the log and the visitor sees a blank page.
Steps to reproduce
Install domain_config, domain_config_ui and locale, then request any page. Every route returns a 500, including the front page. Command line access through drush keeps working, because that container never builds the request stack the loop runs through.
Why no test caught it
The loop needs three things at once: domain_config_ui installed, locale installed, and a real web request. Kernel tests never install domain_config_ui alongside the override, so the decorator is absent. Seven functional and functional javascript tests do install domain_config_ui together with language, but string_translator.locale.lookup comes from locale, and no test in the project installs that module with domain_config_ui. The combination simply is not covered, so the pipelines were honestly green.
Proposed resolution
Resolve the module handler lazily inside getPinnedKeys() instead of taking it as a constructor argument, and drop the argument from the service definition. The surrounding class already does exactly this, for exactly this reason, in getDomainStorage() and installDomainOverrides().
Add a functional test that installs domain_config_ui and locale together and requests a page, so this combination is covered from now on. That gap is the real defect here: a service wiring mistake should not be able to reach a release.
Remaining tasks
Fix on 4.x and backport to 3.x. Both branches carry the identical wiring.
Release 3.1.0-alpha6 promptly, since alpha5 breaks any site with both modules installed.
User interface changes
None.
API changes
DomainConfigFactoryOverride::__construct() loses the module_handler argument added in 3.1.0-alpha5. It was present only in that release.
Data model changes
None.
AI-Generated: Yes (Claude Code was used to help draft this issue summary and wrote the change that caused the regression. I reviewed and reproduced the failure and the fix on a real site before posting.)
Issue fork domain-3618193
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
mably commentedComment #7
mably commented