Chat on WhatsApp
Upgrades & Patches 11 min read

Magento Composer Dependency Conflicts — Diagnose & Fix in 15 Minutes

Every Magento 2.4.7 to 2.4.9 upgrade collides with a composer dependency conflict — laminas-mail pins, symfony/console majors, or php-amqplib downgrades. The official docs recommend hours of trial and error. The faster workflow is `composer why-not`: one command that surfaces the actual blocker in under 60 seconds. This post walks through three production conflict shapes we hit during 2026 upgrades, the exact `composer why-not` invocations, the root-constraint adjustments in `composer.json`, and when to reach for cweagans/composer-patches instead of a version bump. Real package names, real conflict output, real fixes.

Magento Composer Dependency Conflicts — Diagnose & Fix in 15 Minutes

Magento composer dependency conflicts are the predictable set of resolver failures that occur when running composer require magento/product-community-edition:2.4.9 --with-all-dependencies against a project last touched on 2.4.7 or 2.4.8 in 2026 that happens because Adobe bumped symfony/*, laminas/*, and php-amqplib/php-amqplib across major versions in a single release. The fix is a three-step workflow built around composer why-not — here are the three conflict shapes we hit on every upgrade and the exact commands to resolve each in under 15 minutes.

composer why-not is the only diagnostic you need.

Most Magento upgrade post-mortems describe hours of trial-and-error composer require attempts. That workflow is wrong. Composer ships a single command — composer why-not — that walks the resolver graph in reverse and tells you exactly which installed package is blocking the upgrade target.[1]

If you are running composer update in a loop and reading the output by hand, you are debugging the symptom. composer why-not names the cause.

The invocation is always the same shape:

composer why-not <package> <target-version>

For a Magento upgrade the target is always the same:

composer why-not magento/product-community-edition 2.4.9

The output names every installed package whose constraints prevent the resolver from accepting 2.4.9. That list is the entire blocker set. Fix those packages, re-run, done.

Of the 14 Magento 2.4.9 upgrades we have shipped from kishansavaliya.com since the release dropped, every single one hit at least one of the three conflict shapes below. Across all 14, the average wall-clock time from running composer why-not to a clean composer install was 12 minutes.

Conflict shape A: laminas-mail version pin

The most common 2.4.9 conflict is on laminas/laminas-mail. Magento 2.4.9 bumped its requirement to ^2.25; many third-party modules and even some older Magento dependency manifests still pin to ~2.22.0 or ^2.20.

The composer output

$ composer require magento/product-community-edition:2.4.9 --no-update
$ composer update --with-all-dependencies --dry-run

Problem 1
  - magento/product-community-edition 2.4.9 requires laminas/laminas-mail ^2.25
    -> found laminas/laminas-mail[2.25.0, ..., 2.26.x-dev] but the package is fixed to 2.22.0
       (lock file version) by a partial update.
  - Root composer.json requires magento/product-community-edition 2.4.9

The diagnostic

composer why-not laminas/laminas-mail 2.25

laminas/laminas-mail 2.25 requires php ^8.1 (matches your PHP 8.4)
magento/module-email 100.4.5 requires laminas/laminas-mail ~2.22.0 (does not match)
vendor/module-newsletter-extra 1.4.2 requires laminas/laminas-mail ^2.20 (matches)

The blocker is magento/module-email 100.4.5 — Magento's own bundled module that was not bumped because the project is still pinned to the 2.4.8 root metapackage. The fix is the bump itself, but composer is refusing to take it as a partial update.

The fix

Force composer to update the magento metapackage transitively, not as a partial:

composer require magento/product-community-edition:2.4.9 \
  --update-with-all-dependencies \
  --no-update

composer update \
  magento/product-community-edition \
  --with-all-dependencies

The --with-all-dependencies flag is the one most engineers miss. Without it, composer treats the metapackage as a leaf node and refuses to bump magento/module-email. With it, the entire magento/* tree updates atomically.[2]

When you need the cweagans patch instead

If you also depend on a third-party module that pins laminas/laminas-mail to ~2.22.0 and the vendor has not released a 2.4.9-compatible build, you cannot bump laminas-mail without breaking that module. In that case the fix is to patch the vendor's composer.json to relax the constraint.

{
  "require": {
    "cweagans/composer-patches": "~1.7.3"
  },
  "extra": {
    "composer-exit-on-patch-failure": true,
    "patches": {
      "vendor/module-newsletter-extra": {
        "Relax laminas-mail constraint for Magento 2.4.9":
          "patches/newsletter-extra-laminas-mail.patch"
      }
    }
  }
}

The patch file itself is a one-line diff against the vendor's composer.json:

--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,7 @@
   "require": {
     "php": "~8.1.0||~8.2.0||~8.3.0||~8.4.0",
-    "laminas/laminas-mail": "~2.22.0"
+    "laminas/laminas-mail": "^2.20"
   }

Run composer install and the patch is applied to the vendor file in vendor/vendor/module-newsletter-extra/composer.json. The constraint widens, the resolver accepts 2.4.9, and the merchant does not have to wait for the vendor to ship a 2.4.9 release.

Conflict shape B: symfony/console major bump

Magento 2.4.9 bumped symfony/console from ^6.4 to ^7.0. The Symfony 7 major dropped several deprecated public APIs that third-party CLI modules used.

The composer output

Problem 1
  - magento/framework 103.0.9 requires symfony/console ^7.0
  - vendor/module-import-cli 2.3.0 requires symfony/console ^6.4
  - Conclusion: don't install vendor/module-import-cli 2.3.0

The diagnostic

composer why-not symfony/console 7.0

vendor/module-import-cli 2.3.0 requires symfony/console ^6.4 (does not match ^7.0)
vendor/module-export-cli 1.8.4 requires symfony/console ^6.0 || ^7.0 (matches)
magento/framework 103.0.9 requires symfony/console ^7.0 (matches)

One blocker: vendor/module-import-cli. The fix path depends on whether the module's Console\Command class actually uses any Symfony 6-only API.

The fix (option 1: vendor has a 2.4.9 release)

Check the vendor's release notes. If they shipped a 2.4.9-compatible build:

composer require vendor/module-import-cli:^3.0 --no-update
composer update --with-all-dependencies

The fix (option 2: vendor is silent, code is compatible)

Use composer why-not to confirm the only blocker is the composer.json constraint string, not actual API usage. Read the module's Console/Command/*.php files. If they only use InputArgument, InputOption, OutputInterface::writeln, and the Command base class, they are Symfony 7-compatible — the constraint is over-restrictive.

Patch the constraint with cweagans:

{
  "extra": {
    "patches": {
      "vendor/module-import-cli": {
        "Allow Symfony 7 for Magento 2.4.9":
          "patches/import-cli-symfony-7.patch"
      }
    }
  }
}
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
-    "symfony/console": "^6.4"
+    "symfony/console": "^6.4 || ^7.0"
   }

The fix (option 3: vendor is silent, code is incompatible)

If the module calls a Symfony 6-only API (most commonly SymfonyStyle::askHidden's removed signature, or Question::setNormalizer's contract change), you have two paths: write a code-level patch that adapts the call site, or replace the extension. We default to the code-level patch when the merchant has a custom dependency on the extension's data model; otherwise we strip and replace.

Conflict shape C: php-amqplib downgrade request

The most surprising shape: an old Stripe extension still pinned php-amqplib/php-amqplib to ~2.12.0. Magento 2.4.9 bumped to ^3.7. The resolver does not downgrade transitively, so composer simply refuses to install anything.

The composer output

Problem 1
  - magento/module-message-queue requires php-amqplib/php-amqplib ^3.7
  - vendor/module-stripe-payments 4.1.2 requires php-amqplib/php-amqplib ~2.12.0
  - Conclusion: don't install magento/module-message-queue

The diagnostic

composer why-not php-amqplib/php-amqplib 3.7

vendor/module-stripe-payments 4.1.2 requires php-amqplib/php-amqplib ~2.12.0 (does not match)

The blocker is vendor/module-stripe-payments 4.1.2. The vendor shipped a 5.x release in 2025 that bumped to ^3.0, but the merchant never updated.

The fix

composer require vendor/module-stripe-payments:^5.0 --no-update
composer require magento/product-community-edition:2.4.9 --no-update
composer update --with-all-dependencies

If the vendor has an unreleased 4.x build that supports php-amqplib 3.x (we have seen this with two Stripe forks), pin to the dev branch temporarily:

composer require vendor/module-stripe-payments:dev-2.4.9-compat --no-update

Then bump to the stable 5.x once it ships.

The symptom-command-resolution matrix

Keep this table next to your terminal during any 2.4.9 upgrade window.[3]

Symptom in composer outputDiagnostic commandResolution
requires laminas/laminas-mail ^2.25 but lock file pins 2.22composer why-not laminas/laminas-mail 2.25composer update magento/product-community-edition --with-all-dependencies
requires symfony/console ^7.0 + third-party module pins ^6.4composer why-not symfony/console 7.0Bump vendor module to 2.4.9 release, OR patch composer.json constraint via cweagans
requires php-amqplib/php-amqplib ^3.7 + extension pins ~2.12composer why-not php-amqplib/php-amqplib 3.7Bump payment extension to current major version
Generic Conclusion: don't install magento/product-community-editioncomposer why-not magento/product-community-edition 2.4.9Read the package list output — every name is a blocker
requires monolog/monolog ^3.7 conflictcomposer why-not monolog/monolog 3.7Likely an Adobe Commerce-only extension on Magento Open Source; remove or replace

The 15-minute workflow end to end

The full diagnose-and-fix loop, timed against the production upgrades we ran:

  1. Run composer require magento/product-community-edition:2.4.9 --no-update to update the root metapackage line. (30 seconds)
  2. Run composer update --dry-run. Read the first Problem N block. (60 seconds)
  3. Run composer why-not <blocked-package> <target-version>. Note the named blockers. (30 seconds)
  4. For each named blocker: check vendor release notes for a 2.4.9 build. (3 minutes)
  5. If a release exists, composer require vendor/module:^new-major --no-update. If not, write a cweagans patch against the vendor's composer.json. (5 minutes)
  6. Re-run composer update --with-all-dependencies --dry-run. If clean, drop --dry-run. (2 minutes)
  7. If a second Problem N appears, loop back to step 3. (typically 1 iteration, occasionally 2)

Total clean-run wall-clock: 12-15 minutes for a single conflict shape, 25-30 minutes if two shapes hit the same upgrade.

What the official Magento docs get wrong

Adobe's official 2.4.9 upgrade guide recommends running composer require magento/product-community-edition:2.4.9 --no-update followed by composer update. The guide does not mention composer why-not at all. Without it, the merchant reads the cryptic Conclusion: don't install output and guesses which package to bump. The guess is wrong roughly half the time, and the upgrade window blows from 30 minutes to 4 hours.

composer why-not is in the official Composer documentation. It is not in Adobe's official Magento upgrade documentation. Read both.

When cweagans/composer-patches is the wrong tool

The cweagans patch is a band-aid, not a fix. Use it when:

  • The vendor will ship a 2.4.9 release in the next 2-4 weeks and you need to upgrade now.
  • The constraint is over-restrictive but the actual code is compatible (most common shape).
  • The merchant is on a fixed-price contract with the vendor and waiting for support is contractual.

Do not use it when:

  • The vendor module has actually broken on the new dependency (you are patching constraint, not behavior — the runtime will crash).
  • The vendor has explicitly dropped support for Magento 2.4.9 (the patch hides a real incompatibility).
  • The patch file is more than 50 lines (you are forking, not patching — own the fork or replace the extension).

Pinning the resolver for reproducible builds

Two flags every Magento composer.json should set, regardless of the conflict story:

{
  "config": {
    "sort-packages": true,
    "allow-plugins": {
      "cweagans/composer-patches": true,
      "magento/composer-dependency-version-audit-plugin": true,
      "laminas/laminas-dependency-plugin": true,
      "magento/composer-root-update-plugin": true,
      "dealerdirect/phpcodesniffer-composer-installer": true
    },
    "preferred-install": {
      "*": "dist"
    }
  },
  "minimum-stability": "stable",
  "prefer-stable": true
}

The magento/composer-root-update-plugin is what Adobe ships to handle root-level updates of the metapackage. It is enabled by default on new Magento installs but often missing on projects that started life on 2.4.4 or earlier.[4]

The composer.lock conversation

Commit composer.lock. Always. We have lost two production weekends to a merchant who deleted composer.lock on the assumption that "composer will figure it out". It does not — without a lock file, the resolver picks the newest mutually-compatible versions, which is rarely the set you tested on staging.

The corollary: when the conflict workflow above produces a clean install, commit the new lock file immediately. Stage and production must run from the same lock or you are upgrading two different stores.

FAQ

Why does composer keep saying "don't install magento/product-community-edition" with no explanation?

It is explaining — just badly. The Problem 1 block above the conclusion names the actual conflict. Read it. If the block names a package you do not recognize, run composer why-not <that-package> <version-from-the-message>.

Should I delete composer.lock and start fresh?No. Deleting composer.lock turns a 15-minute diagnostic into a 4-hour resolver hunt with no reproducibility. Use composer why-not against the existing lock.

Why does cweagans/composer-patches need composer-exit-on-patch-failure?

Without it, a patch that fails to apply (because the upstream composer.json changed format) is treated as a warning and composer continues. You end up with an install that succeeded in CI but is missing the patch in production. composer-exit-on-patch-failure: true makes the install fail loudly.

Can I use composer why-not on Magento Cloud?

Yes — it runs against the same composer.lock. SSH into the build container (magento-cloud ssh) or run it locally against the cloud project's lock file. The output is identical.

What's the difference between composer why and composer why-not?

composer why <package> answers "what installed packages depend on this one". composer why-not <package> <version> answers "why can't this version be installed". Both walk the same dependency graph, opposite directions.[5]

Will Adobe fix these conflicts upstream?

Some of them. The laminas-mail pin was relaxed in a 2.4.9-p1 patch release. The Symfony 7 bump is permanent. The php-amqplib bump is permanent. Plan to handle these classes of conflict on every major Magento release, not just 2.4.9.

What about Magento 2.4.4 to 2.4.8 upgrades?

Same workflow, different package set. Magento 2.4.4 — 2.4.9 upgrades all benefit from composer why-not. The 2.4.6 to 2.4.7 jump trips on elasticsearch/elasticsearch 7.x to 8.x; 2.4.7 to 2.4.8 trips on monolog/monolog 2 to 3; 2.4.8 to 2.4.9 trips on the three shapes above. Same diagnostic, different blockers.

Is there a CI check I can run to catch these before merge?

Yes — add composer validate --strict --no-check-publish and a composer update --dry-run step to your CI pipeline. The first catches malformed composer.json files; the second surfaces resolver conflicts before they reach the production deploy script.

References

  1. Composer documentation, why-not (also known as prohibits) command, getcomposer.org/doc/03-cli.md.
  2. Composer documentation, update / upgrade command, --with-all-dependencies flag, getcomposer.org/doc/03-cli.md.
  3. Adobe Commerce DevDocs, Upgrade to Magento 2.4.9, experienceleague.adobe.com.
  4. Magento composer-root-update-plugin, github.com/magento/composer-root-update-plugin.
  5. Composer documentation, depends (why) and prohibits (why-not) commands, getcomposer.org/doc/03-cli.md.
Stuck on a Magento composer conflict at 11pm on a Friday?

I run fixed-scope Magento 2.4.4 — 2.4.9 upgrade sprints with a documented composer diagnostic pass, cweagans patch repository, and rollback plan. Fixed quote from $499 audit / $2,499 sprint / ~28h @ $25/hr. See hire me or the Magento upgrade service.