Upgrading this site to Drupal 11, the very first command I ran should have been the anticlimactic one: composer require drupal/core-recommended:^11. Instead it failed immediately, and the reason it gave was, technically, true and completely useless at the same time.
What composer actually said
Forty-some lines of the same shape, one per patch release of Drupal 11, none of them telling me anything new:
drupal/core-recommended 11.0.0 requires drupal/core 11.0.0 -> found drupal/core[11.0.0] but these were not loaded, likely because it conflicts with another require.drupal/core-recommended 11.0.1 requires drupal/core 11.0.1 -> found drupal/core[11.0.1] but these were not loaded, likely because it conflicts with another require.
...and so on, through every 11.x release that exists. The phrase doing all the damage is "likely because it conflicts with another require" — composer knows a conflict exists, and it's telling you it knows, without telling you what the conflict actually is. It's not being evasive. Its own solver genuinely arrived at "unsatisfiable" without necessarily retaining a clean, human-readable trace of which single constraint broke everything else. You get the symptom, repeated for every version it tried, and the cause is left as an exercise for you.
The instinct that wastes the first ten minutes
My first move was composer why-not drupal/core 11.4.5, on the theory that if why tells you why something's installed, why-not should tell you why something isn't installable. It returned nothing but a suggestion to try a dry-run install instead — which, to be fair, turned out to be the right suggestion, just not in the shape I expected. Running composer require "drupal/core:11.4.5" --dry-run produced a longer, differently-shaped wall of text: one "Problem" entry per already-locked package, each one showing that package's own drupal/core version constraint next to the list of Drupal 11 releases it supposedly couldn't be loaded alongside.
That's more information, but it's still not the answer — it's fifteen packages, all restating the same "conflicts with something" without saying which one of the fifteen is the actual load-bearing constraint and which are just innocent bystanders getting swept into the same unsatisfiable set.
What actually worked
Composer's own output describes the failure from the solver's point of view: a giant, unordered set of constraints that don't all fit together. What it doesn't do is rank them by relevance. The only way to find the one constraint that actually mattered was to stop reading composer's prose and go straight at the data it was reasoning over — the lock file itself — and ask a narrower, more mechanical question than composer's error message was answering: which installed package's require section doesn't even list Drupal 11 as an option at all, full stop, independent of any specific patch version.
That's a five-line script against composer.lock, not a conversation with the CLI. It found one real answer immediately: a module whose only available release still declared support for Drupal 9 and 10 and nothing newer. Every one of those forty error lines had been composer correctly, faithfully reporting the downstream consequences of that one line in one file, without ever pointing at it directly.
The second time it happened, I already knew what to do
After removing that blocker, the exact same wall of text came back, word for word, just about a different transitive dependency — symfony/console this time instead of drupal/core directly. Same shape of message, same non-answer about which specific requirement was actually load-bearing. This time the script took thirty seconds instead of ten minutes, because the technique doesn't change: composer's conflict report tells you a solution doesn't exist. It was never going to be the tool that tells you which single line is why, and expecting it to is the part that actually costs you time.