Apply Drupal 9 compatibility patches with Composer

Photo of assorted patches
The vast majority of community-contributed Drupal 8 modules now have releases that are compatible with Drupal 9, but what can you do if you need to use a module that doesn’t? Well, you’re likely to find a compatibility patch in the project’s issue queue. But the tool most of us use with composer to apply patches, cweagans/composer-patches, is a plugin that can only make its changes after composer reads a package’s metadata about its compatibility (and dependencies). So for contrib modules that haven’t yet committed their patches, attempting to apply the patch in the usual way doesn’t help. For example, using composer to patch commerce_migrate for Drupal 9 compatibility produces an enormous and confusing error:

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: don't install drupal/commerce_migrate 2.0.0-rc3
    - Conclusion: don't install drupal/commerce_migrate 2.0.0-rc2
    - Conclusion: remove drupal/core 9.0.9
    - Installation request for drupal/commerce_migrate ^2.0@RC -> satisfiable by drupal/commerce_migrate[2.0.0-rc1, 2.0.0-rc2, 2.0.0-rc3].
    - Conclusion: don't install drupal/core 9.0.9
    - drupal/commerce_migrate 2.0.0-rc1 requires drupal/core ^8.7 -> satisfiable by drupal/core[8.7.0, 8.7.0-alpha1, 8.7.0-alpha2, 8.7.0-beta1, 8.7.0-beta2, 8.7.0-rc1, 8.7.1, 8.7.10, 8.7.11, 8.7.12, 8.7.13, 8.7.14, 8.7.2, 8.7.3, 8.7.4, 8.7.5, 8.7.6, 8.7.7, 8.7.8, 8.7.9, 8.8.0, 8.8.0-alpha1, 8.8.0-beta1, 8.8.0-rc1, 8.8.1, 8.8.10, 8.8.11, 8.8.12, 8.8.2, 8.8.3, 8.8.4, 8.8.5, 8.8.6, 8.8.7, 8.8.8, 8.8.9, 8.9.0, 8.9.0-beta1, 8.9.0-beta2, 8.9.0-beta3, 8.9.0-rc1, 8.9.1, 8.9.10, 8.9.11, 8.9.2, 8.9.3, 8.9.4, 8.9.5, 8.9.6, 8.9.7, 8.9.8, 8.9.9].
    - Can only install one of: drupal/core[8.7.0, 9.0.9].
    - Can only install one of: drupal/core[8.7.0-alpha1, 9.0.9].
    - Installation request for drupal/core (locked at 9.0.9) -> satisfiable by drupal/core[9.0.9].

Installation failed, reverting ./composer.json to its original content.

This message goes on and on, including scary messages like ‘Conclusion: remove drupal/core‘, and sometimes about all sorts of seemingly unrelated packages! Ultimately, composer is trying to bend over backwards to use versions of Drupal and its dependencies that would work with the module, but it won’t find any. Or if it does, you might find yourself downgraded to an old version of Drupal, which you certainly don’t want!

So what’s the solution?

We need to override the compatibility metadata for the contrib module.

Drupal sites use as the metadata provider, which you’ll find listed in your project’s root composer.json file under a repositories section. This section instructs composer where to get the metadata from … so if you add your own ‘package’ repository to be used first, composer will happily use that, ignoring what tells it:

"repositories": [
      "type": "package",
      "package": {
        "name": "drupal/commerce_migrate",
        "type": "drupal-module",
        "version": "dev-8.x-2.x",
        "source": {
          "type": "git",
          "url": "",
          "reference": "9ac26262b3443d20e69cea69652abc2dee39fee5"
      "type": "composer",
      "url": ""

In this example, for the commerce_migrate module, we specify the latest commit from its development branch (8.x-2.x), as that’s what the patch is intended to get ultimately applied to. That branch also matches our requirement in our composer.json file: "drupal/commerce_migrate": "dev-8.x-2.x". (Note that composer needs the dev- prefix when using git branches.)

So this section redefines the module’s dev-8.x-2.x version for composer. As we require that version, we get the snapshot of the codebase at that specific commit – skipping the metadata that would have added which would restrict its compatibility.

Now, the patch can be added in the usual patches section, and then running composer update drupal/commerce_migrate will apply it successfully!

This technique can also be used to override other package metadata for composer, such as a change in dependencies. But beware – it means you’re no longer using the real releases for the package, and in this example, locks to a specific commit. So you will need to keep an eye out for when the patch gets committed to the module, and for any other work in its codebase that should be incorporated over time. It’s a little like taking a snapshot of the project, and applying just the specific changes you need – at the cost of losing out on any goodness that the maintainers may add over time. This approach does avoid forking the codebase away at least. As this replaces the module’s metadata entirely, it’s worth checking for any dependencies or other compatibility metadata that its own (potentially now patched) composer.json file might have, and copying them to your project’s root composer.json file.

Given how quickly the community & its leaders have rallied to make modules D9-ready, I wouldn’t be surprised if this trick can be removed in a few months’ time, as maintainers continue to commit these (often trivial) patches. Any projects that don’t, may even get covered automatically some day – plus, if their maintainers aren’t doing this, they’re also unlikely to be adding much of note in the meantime anyway, so it’s pretty safe. I would just keep an eye out for any new releases – especially security ones – to those projects.

Get notified

I suggest finding the issue that currently tracks making the module compatible with Drupal 9. This may be linked to from the project page, or it’s usually one of the more recent/active ones in its issue queue. If you’re logged into (and you should be – sign up if you haven’t!), then you can press a ‘Follow’ link that has a nice green star next to it, to get email notifications of updates. The ‘View all releases’ link from a project page takes you to a page that has an RSS feed, which can be used to subscribe to notifications of new releases too. That’s well worth doing for a number of reasons! If you didn’t know it, you can also subscribe to notifications of security releases – head to where you’ll find instructions at the top of the sidebar (which is below the list of posts if you’re on a mobile).


Thanks to heddn, dpi and larowlan for this solution. Photo by Josh Carter on Unsplash.