Extension Guard
This module prevents configuration exports when required modules are not enabled, addressing a common Drupal issue where dependencies are added but never activated. It provides a status report and blocks exports until the problem is resolved, offering clearer error messages than core Drupal.
Extension Guard finds modules your site requires but never turned on — a silent problem that normally surfaces months later as a failed deployment. It blocks configuration exports until the problem is fixed, and reports it on the status report in the meantime.
Here is how the problem happens. Every Drupal module lists the other modules it needs in order to work. Drupal checks that list once, at the moment you turn the module on, and never again. So when you update a module and the new version needs something extra, Drupal downloads that extra module but does not turn it on, and does not tell you. Your site keeps working perfectly.
Months later, someone installs or removes an unrelated module, Drupal finally re-checks, and a deployment fails with an error naming a module that has nothing to do with the problem. Nothing you ran in between could have warned you:
What you ran Why it did not noticecomposer install
Composer did its job correctly. The module is downloaded and on disk. It just was never turned on.
drush status
Never looks at module dependencies at all.
drush config:status
The module is missing from your site and from your exported configuration, in exactly the same way. So there is no difference to report.
drush config:import
Only validates modules it is currently installing. If it is installing nothing, it checks nothing.
drush core:requirements
Only runs checks belonging to modules that are turned on.
drush config:export then git diff
The gap is part of your site's real state, so it exports cleanly and there is nothing unusual to review.
This is core issue #799314, a Major bug report that has been open since 2010. Drupal core ran into it itself in #3099746.
Features
- Blocks
drush config:exportwhen a module your site needs is not turned on. This is the moment that matters: it is the command you already run aftercomposer update, so there is no new step to remember and no new habit to build. The broken state never reaches your repository. - A read-only check,
drush extension-guard:check, which reports the same thing and exits non-zero. Use it in CI, or to inspect a production site without exporting anything. - A status report entry that shows the problem continuously, so an existing inconsistency is visible without running any command.
- Names the module actually at fault. Drupal's own error message names whichever module happened to trigger the check, which can be several steps removed from the real cause. This module names the module that declares the missing dependency, and lists everything that depends on it.
- Detects malformed dependency declarations. An info.yml file whose dependency list is written without the leading hyphen is parsed as plain text and silently discarded by Drupal, so that dependency is never enforced at all. That is a bug in the module itself, so it is reported as a warning rather than blocking you.
When would I use this? Any site where configuration is exported to code and imported on deployment, which is essentially any site with a staging or production environment. The risk grows with the number of contributed modules you have and the frequency of composer update.
Typical use cases: catching the problem on a developer machine right after a dependency update; failing a CI build before a release is tagged; and auditing an existing production site, where this class of problem has often been sitting unnoticed for months.
Post-Installation
There is nothing to configure and no page to visit. The module starts working the moment you install it.
Two things happen immediately:
- Visit Reports, then Status report. You will see a Module dependency closure entry: green if everything your site needs is turned on, red if it is not.
- Run
drush cex. If something is missing, the export is refused and you are told exactly what to do.
$ drush cex
[error] The module dependency graph is not closed.
Required by an installed module, but not installed:
svg_image (required by focal_point_extras)
paragraphs_library (required by my_layouts, my_blocks)
This usually means a package update added a dependency to a module that
was already installed. Drupal does not enable it for you, and no other
command reports it.
Fix: drush pm:install svg_image,paragraphs_library
Then: drush config:export
Run the suggested command, export again, and commit the resulting core.extension.yml change along with your composer.lock.
Recording deliberate exceptions. Occasionally an unmet dependency is intentional, usually an upstream packaging mistake where a module declares a dependency that duplicates something your site already has. Record those instead of switching the check off:
drush config:set extension_guard.settings ignored_modules.0 some_module
Guarding deployments as well (optional). drush config:import is deliberately not guarded by default, because it runs inside drush deploy, and a site that already has this problem would find its deployments blocked with no warning. Turn it on once you know your production site is clean:
drush config:set extension_guard.settings enforce_on_config_import 1
Bypassing once. If you need an export before you have finished resolving things:
EXTENSION_GUARD_SKIP=1 drush cex
An environment variable is used deliberately, rather than a yes/no prompt. Exports and deployments normally run with the -y flag, and a prompt would answer itself and silently disable the check.
All settings live in extension_guard.settings. There is no administrative form yet. See the issue queue if you would like one.
Additional Requirements
- Drupal ^11.2. The module uses
hook_runtime_requirements()and theRequirementSeverityenum, both introduced in Drupal 11. - Drush ^13. Required, not optional: the
config:exportguard is implemented as a Drush command validator. Sites that do not use Drush will get the status report entry only.
No external libraries, APIs or services are needed.
Recommended modules/libraries
Nothing is required to get full functionality. These are complementary rather than enhancing:
- Module Missing Message Fixer handles the opposite problem: modules recorded as installed that are missing from disk. Between the two, both directions of the inconsistency are covered.
- Site Audit provides broad site auditing, including unmaintained and unused modules. Useful alongside this, and it does not overlap.
Similar projects
As far as I could determine, nothing else performs this specific check. The closest projects, and how they differ:
- Module Missing Message Fixer solves the mirror image: modules listed in
core.extensionthat are absent from the filesystem. It explicitly does not check whether an installed module's dependencies are turned on. - Site Audit inspects the installed module landscape, flagging unmaintained, dev-only and apparently unused modules, but not unsatisfied ones.
- Configuration Update Manager compares individual configuration items against the defaults their modules ship. It works one level below this: it assumes the module list itself is correct.
- Configuration Inspector validates configuration against its schema. A different question entirely.
- Config Enforce changes which copy of your configuration is authoritative. It does not examine module dependencies.
If you know of a project that does cover this, please open an issue. I would rather contribute there than duplicate it.
Supporting this Module
There is no funding link. The most useful support is a bug report: this module reads dependency data from every extension on your site, and real sites contain declarations that are stranger than anything I could invent. The malformed-declaration detection exists because a widely used contributed module turned out to have one.
The other way to help is upstream. The runtime check here is what core issue #799314 asked for in 2010 and never got. Support on that issue is worth more in the long run than this module is.
Community Documentation
None yet. If you write a walkthrough or record a video, please open an issue and it will be linked here.
Background reading on the underlying behaviour:
- #799314 Checking/resolving new dependencies for updated modules. Major, open since 2010.
- #3099746 When importing configuration, enable modules' dependencies even if they are not specified.
- #3432353 Add validation constraints to core.extension. Added a constraint checking that each listed module exists on disk, but not that the list is complete.
Design notes
Only directly declared dependencies are examined, not Drupal's computed transitive dependency list. The latter is the full closure, and using it would report a single missing module once for every installed module that can reach it, dozens of times on a large site, with the actual culprit buried. A module list that is complete with respect to declared dependencies is automatically complete with respect to transitive ones, so this is both more precise and equally thorough.
drush deploy is not hooked, and does not need to be. It runs without bootstrapping Drupal, so module-supplied commands are never discovered there, and it performs no work of its own: it runs each step as a separate Drush process. Guarding config:import covers the deployment path precisely.