Short answer: to resolve a Composer dependency conflict, read the last line of each Problem block first. It names the package, the version range that package needs, and the constraint that blocks it. Then run composer why-not <package> <version> to confirm who is blocking, and fix it in one of three ways: upgrade the blocking package, loosen your own constraint in composer.json, or let Composer move locked packages with composer update <package> --with-all-dependencies. Preview with --dry-run and run your tests afterward.
Everything below targets Composer 2.x on PHP 8.1 or later. The --minimal-changes flag needs Composer 2.7 or newer. Run composer --version before you start, because Composer 1 prints different, less helpful messages.
What “could not be resolved to an installable set of packages” actually means
Composer is a solver. Every package in your tree declares version ranges for the packages it needs, and your root composer.json adds its own. Composer searches for one version of every package that satisfies every range at the same time. When no such combination exists, it stops and prints:
Your requirements could not be resolved to an installable set of packages.
That sentence tells you nothing on its own. The useful part is the list of Problem blocks underneath it. Each block is a chain of reasoning, written top to bottom, that ends at the contradiction Composer could not get past.
Here is an illustrative example. acme/payments is a made-up package; the output format is the one Composer 2 prints.
Problem 1
- Root composer.json requires acme/payments ^2.0 -> satisfiable by acme/payments[2.0.0, 2.1.0].
- acme/payments[2.0.0, ..., 2.1.0] require guzzlehttp/guzzle ^6.5 -> found guzzlehttp/guzzle[6.5.0, ..., 6.5.8] but it conflicts with your root composer.json require (^7.8).
Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.
Read it as a sentence. Line one: you asked for acme/payments ^2.0, and versions 2.0.0 and 2.1.0 exist. Line two: every one of those versions needs Guzzle 6.5, but your own composer.json demands Guzzle 7.8 or higher. Nothing can be both, so the install fails.
Read the Problem block from the bottom up
The first line of a block is almost always your own requirement, which is the least interesting part. The last line is where the contradiction lives. Start there, then walk upward only if you need the chain that led to it.
This matters more than it sounds. The package you just tried to install is often not the package you need to change, much like how the error line is not where the bug is in a PHP stack trace. In the example above, you ran composer require acme/payments, but the fix may be upgrading Guzzle’s consumer, not touching Guzzle at all.
When there are several Problem blocks, read all of them before acting. Composer lists every dead end it hit, and two blocks often describe the same root cause from different sides. Fixing the one that names a package you control directly usually clears the others.
A decoder for the phrases Composer uses
Composer reuses a small set of phrases. Once you know them, most messages read in a few seconds.
| Phrase in the Problem line | What it means | First thing to try |
|---|---|---|
Root composer.json requires X ^N -> satisfiable by X[...] | Your own requirement. Versions exist. | Nothing yet. Read the next line. |
found X[...] but it conflicts with your root composer.json require (^N) | A dependency needs a range of X that excludes the range you set directly. | Upgrade the dependency to a release that supports your range, or relax your own constraint. |
found X[...] but these were not loaded, likely because it conflicts with another require | Those versions exist but were ruled out by some other requirement, often described in a different Problem block. | Run composer why-not X <version> to find the other requirement. |
but the package is fixed to N (lock file version) by a partial update | You updated one package, and Composer refused to move another one that composer.lock pins. | Add that package to the update command, or use -w / -W. |
your php version (N) does not satisfy that requirement | The PHP running Composer is older or newer than a package allows. | Check php -v in the same terminal, then pick a package release that supports it. |
requires ext-intl * -> it is missing from your system | A PHP extension is not enabled for the CLI. | Run php --ini, enable the extension in the file it lists, and retry. |
could not be found in any version | Typo, a private package without its repository configured, or a stability setting that hides the only releases. | Check the name on Packagist and your minimum-stability. |
The “these were not loaded” line deserves a warning. It sounds like a network or cache problem, and it almost never is. It means the solver already excluded those versions for a reason that is printed somewhere else.
Ask Composer who is blocking, instead of guessing
Composer ships two commands that answer “why” directly. The Composer command-line documentation, which we read on September 23, 2026, describes prohibits (alias why-not) as the command that shows which packages block a given package or version, and depends (alias why) as the one that shows which packages require it.
# Who stops me from installing Guzzle 7.8.0?
composer why-not guzzlehttp/guzzle 7.8.0
# Who depends on Guzzle at all, shown as a tree up to the root?
composer why guzzlehttp/guzzle --tree
# Which versions of the blocking package exist?
composer show acme/payments --all
why-not works against what is installed and locked, so run it in a project that has a composer.lock. The --tree flag (short -t) prints the full path from the package up to your root composer.json, which is the fastest way to spot a deep transitive dependency you never required yourself.
composer show <package> --all lists the versions available in your configured repositories. Use it to check whether a release that supports your constraint exists at all. If it does not, no flag will fix the conflict; you need a different package, a fork, or a different constraint.
How to resolve a Composer dependency conflict, step by step
Once you know which package is blocking and why, the fix is usually one of these, in order of how little they disturb your project.
1. Upgrade the blocking package. If composer show acme/payments --all lists a newer release that supports Guzzle 7, raise the constraint and update only that package plus what it needs:
composer require acme/payments:^3.0 --with-all-dependencies --dry-run
Drop --dry-run once the preview looks right. The dry run prints the installs, updates and removals Composer would make without touching vendor/ or composer.lock.
2. Let Composer move locked packages. The hint at the bottom of the error, --with-all-dependencies (-W), lets Composer upgrade, downgrade or remove packages that are currently locked. Per the CLI docs, -w (--with-dependencies) updates the dependencies of the named packages except those that are root requirements, while -W also includes root requirements. On Composer 2.7 and later, add -m (--minimal-changes) so only the packages that must change actually change:
composer update acme/payments -W -m --dry-run
3. Relax your own constraint. If your composer.json says "guzzlehttp/guzzle": "^7.8" but nothing you wrote needs 7.8 specifically, ^7.0 may be enough. Only do this when you know your code does not rely on the newer features.
4. Match the platform to production. If the conflict is about the PHP version, the real question is which PHP your server runs. When your laptop runs a newer PHP than production, pin the platform so Composer resolves for the server:
{
"config": {
"platform": {
"php": "8.2.12"
}
}
}
Set this to the exact version your production server reports. A wrong value here produces a lock file that installs cleanly and then breaks at runtime.
The two shortcuts that make it worse
--ignore-platform-reqs. This tells Composer to skip PHP version and extension checks. The install succeeds, and then the code fails the first time it calls a function your PHP does not have. The narrower --ignore-platform-req=ext-intl is acceptable as a temporary measure in a build container where you know the extension exists at runtime. Anywhere else, fix the platform instead.
Deleting composer.lock and vendor/. This does not resolve a conflict. It turns a targeted update into a full update of every package at once, so you trade one clear error for a pile of unrelated version changes, and sometimes the same error again. Keep the lock file and update the specific packages named in the Problem block.
What your AI assistant will get wrong here
AI coding assistants are useful for explaining a long Problem block, and fast at spotting which line matters. They are also unreliable on the one fact that decides the fix: which versions of a package actually exist and what each one requires. An assistant will cheerfully suggest "acme/payments": "^3.4" for a release that was never tagged, or claim a version supports PHP 8.3 when its composer.json says otherwise.
Treat any version number an assistant gives you the way you would treat a function name it invented. Check it with composer show <package> --all before you edit composer.json, the same habit that helps when the AI invented a function that does not exist. Be especially skeptical when the suggested fix is --ignore-platform-reqs or deleting the lock file; both make the error disappear without fixing anything.
Official documentation beats both us and the assistant on reference detail. The Composer troubleshooting guide, read on September 23, 2026, recommends working with the latest Composer version when you hit any problem, and points to why-not for packages that will not update to the version you expect.
After the fix: check that nothing changed quietly
A successful -W update can downgrade a package you did not name. Composer lists every change as it runs, so read the Downgrading lines, not just the final “Generating autoload files”. A downgrade that passes install can still change behavior, which is the kind of bug that produces code that runs but gives the wrong result instead of an error.
A short checklist after any conflict fix:
- Run
composer validateto confirmcomposer.jsonis still well formed. - Commit
composer.jsonandcomposer.locktogether, never one without the other. - Run your test suite, or at least the paths that touch the packages that moved.
- Run
composer auditto check the new versions against known security advisories. - Deploy with
composer install, notcomposer update, so production gets exactly what you tested.
FAQ
What does “Your requirements could not be resolved to an installable set of packages” mean?
It means Composer could not find one version of every package that satisfies all version constraints at once. The cause is in the Problem lines printed below that sentence. Read the last line of each block first, since it names the package and the constraint that clash.
Is it safe to use composer update -W?
It is safe when you preview it. -W allows upgrades, downgrades and removals of locked packages, so run it with --dry-run first, read the planned changes, and run your tests afterward. On Composer 2.7 or later, adding -m limits the changes to what is strictly necessary.
How do I find which package is blocking an update?
Run composer why-not vendor/package version, for example composer why-not guzzlehttp/guzzle 7.8.0. It prints the packages whose requirements rule out that version. Add --tree to see the full path back to your root composer.json.
Should I use --ignore-platform-reqs to fix a PHP version conflict?
Usually not. It skips the check but not the problem, so the code can fail at runtime on the real PHP version. Choose a package release that supports your PHP, upgrade PHP, or set config.platform.php to the exact version your production server runs.
Why does Composer say a package is “fixed to the lock file version by a partial update”?
You asked Composer to update some packages, and a package outside that list is pinned in composer.lock at a version that no longer fits. Add that package to the update command, or use --with-dependencies or --with-all-dependencies so Composer may move it.

