Fix Composer stable, caret constraint installs dev release

Created on 23 July 2025, 10 days ago

Problem/Motivation

Because there is a 2.3.1 and 2.x release, Composer constraint ^2.3 installs 2.x-dev instead of the stable 2.3.1, because Composer treats the numeric branch 2.x as a higher version than 2.3.1, even with prefer-stable: true.

Explanation

(with some help from ChatGPT)

The caret operator ^2.3 expands to the range >=2.3.0 <3.0.0. Composer builds an internal list of all matching refs (tags 2.3.0, 2.3.1 and the branch “2.x” as a dev branch) and always picks the highest numeric version first, then considers stability.

Editor Advanced Link's 2.x release is treated as a numeric feature branch (2.x-dev), which Composer interprets like “2.999.0‑dev”—higher than 2.3.1—and thus prefers it, even with "prefer-stable": true, because that setting only breaks ties within a given version.

Composer’s resolver works in two clearly documented stages:

  1. Numeric version selection

    “When Composer has a complete list of available versions from your VCS, it then finds the highest version that matches all version constraints in your project … and it downloads … that tag.”
    getcomposer.org

    That means it looks at every matching tag and branch (including dev-2.x), normalizes them (e.g. dev-2.x becomes something like 2.999.0‑dev) and picks the numerically greatest candidate—before looking at stability.

  2. Stability preference only within that chosen version

    Once the highest numeric version is chosen, Composer looks at stability to decide between, say, 2.3.1 vs. 2.3.1‑dev. That’s where prefer-stable: true can nudge you toward the stable tag when two candidates share the same version number. But it never goes back and replaces “2.999.0‑dev” with “2.3.1” because that would be choosing a lower numeric version.

In short, Composer’s “pick highest version” step is strictly numeric, and prefer-stable only applies to break ties within a given normalized version, not to override the numeric ordering itself.

Steps to reproduce

  1. Configure in composer.json:
    "minimum-stability": "dev",
    "prefer-stable": true,
    
  2. Run composer require drupal/editor_advanced_link:^2.3
  3. Observe that Composer installs dev‑2.x instead of the current release 2.3.1.

Proposed resolution

Remove (unpublish) the 2.x dev release (and its branch).

Without the “2.x” dev entry, the ^2.3 constraint will correctly resolve to the latest stable 2.3.1.

Workaround

Rather than the commonly used caret (^) constraint, specify the exact version desired (e.g. 2.3.1).

🐛 Bug report
Status

Active

Version

2.0

Component

Code

Created by

🇺🇸United States jcandan

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024