10aday › Version Constraint Checker › Pre-release versions (beta, rc)

Does ^1.2.0 match 1.3.0-beta? Pre-release versions in five package managers

Short answer: no, not by default. All five package managers skip pre-releases (-beta, -rc, b1, .rc1) unless you ask for them. But “asking” works differently in each one. Put a pre-release in the constraint, and npm and Cargo only open the door for that exact x.y.z. Composer, pip and RubyGems then accept any newer pre-release. And in RubyGems, < 2 lets 2.0.0.rc1 through.

Have a constraint and a list of versions? Paste them into the Version Constraint Checker: it shows, side by side, what npm, Cargo, Composer, pip and RubyGems accept.

What gets installed

Suppose the package has published three versions: 1.2.5, 1.3.0-beta.1 and 2.0.0-rc.1 (written in each tool's own syntax). Each cell shows the constraint and the version the tool picks. Highlighted cells are where one tool behaves differently from the others.

ConstraintnpmCargoComposerpipRubyGems
^1.2.0 (or its equivalent)^1.2.0
→ 1.2.5
^1.2.0
→ 1.2.5
^1.2.0
→ 1.2.5
>=1.2.0,<2
→ 1.2.5
~> 1.2
→ 1.2.5
>=1.0.0>=1.0.0
→ 1.2.5
>=1.0.0
→ 1.2.5
>=1.0.0
→ 1.2.5
>=1.0.0
→ 1.2.5
>= 1.0.0
→ 1.2.5
^1.2.0-beta.1 (a pre-release in the constraint)^1.2.0-beta.1
→ 1.2.5
^1.2.0-beta.1
→ 1.2.5
^1.2.0-beta1
→ 1.3.0-beta1
>=1.2.0b1,<2
→ 1.3.0b1
>= 1.2.0.beta1, < 2
→ 2.0.0.rc1
>=2.0.0-rc.1>=2.0.0-rc.1
→ 2.0.0-rc.1
>=2.0.0-rc.1
→ 2.0.0-rc.1
>=2.0.0-RC1
→ 2.0.0-RC1
>=2.0.0rc1
→ 2.0.0rc1
>= 2.0.0.rc1
→ 2.0.0.rc1
^1.3.0-beta.1^1.3.0-beta.1
→ 1.3.0-beta.1
^1.3.0-beta.1
→ 1.3.0-beta.1
^1.3.0-beta1
→ 1.3.0-beta1
>=1.3.0b1,<2
→ 1.3.0b1
>= 1.3.0.beta1, < 2
→ 2.0.0.rc1
>1.2.5 (only pre-releases are newer)>1.2.5
→ nothing
>1.2.5
→ nothing
>1.2.5
→ nothing
>1.2.5
→ 2.0.0rc1
> 1.2.5
→ nothing

Tested on 2026-11-12 with node-semver (npm's resolver library), the Cargo rules of the semver crate, a real composer update (Composer 2.8, default minimum-stability), Python packaging 26.3 (the library pip uses) and RubyGems 3.5.22. Pip and Composer also look at other packages and options; this is the default case with one dependency.

The rule in each package manager

How to opt in

ToolGet the release candidate of 2.0
npm"^2.0.0-rc.1" in package.json
Cargo"2.0.0-rc.1" in Cargo.toml (a bare version is a caret requirement)
Composer"^2.0@RC", or "^2.0.0-RC1"
pippip install --pre pkg, or pkg>=2.0.0rc1
RubyGemsgem install pkg --pre, or gem "pkg", ">= 2.0.0.rc1" in the Gemfile

To see how each tool orders 1.0.0-1, 1.0.0rc1 or 1.0.0.beta against the final release, paste them into the Version sorter.

Common questions

Does ^1.2.0 match 1.3.0-beta.1 in npm?

No. In npm a pre-release only matches if the range itself contains a pre-release on the same major.minor.patch. ^1.2.0-beta.1 matches 1.2.0-beta.2 but still not 1.3.0-beta.1. Cargo follows the same rule.

How do I install a beta or release candidate?

npm and Cargo: write it in the constraint, e.g. ^2.0.0-rc.1. Composer: ^2.0@RC, a constraint with -RC1, or "minimum-stability". pip: pip install --pre or a specifier that names a pre-release, e.g. >=2.0.0rc1. RubyGems: gem install --pre or a requirement such as >= 2.0.0.rc1.

Why did pip install a release candidate I did not ask for?

pip accepts a pre-release when no final release satisfies the specifier. With 1.2.5 and 2.0.0rc1 published, >1.2.5 installs 2.0.0rc1. Composer, npm, Cargo and RubyGems install nothing in that case.

Does <2.0.0 include 2.0.0-rc.1?

Not in npm, Cargo, Composer or pip (PEP 440 says <V excludes pre-releases of V). In RubyGems, 2.0.0.rc1 sorts below 2.0.0, so < 2 accepts it as soon as pre-releases are allowed.

Why does Composer ^1.2.0-beta1 install 1.3.0-beta1?

Writing -beta1 in a root requirement sets the stability for that package to beta. Every beta in the range is then allowed, and Composer takes the highest one. Use "prefer-stable": true to keep stable releases when one fits.

Related

Sources