Skip to content

/compat/semver/semver-range-syntax

Version range syntax: ^ and ~ in npm, Cargo, Composer and pip

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated 2026-10-20
tool
SemVer
versions
SemVer 2.0.0, PEP 440
os
any

The same characters mean slightly different things in different package managers. ^1.2.3 allows >=1.2.3 <2.0.0 in npm, Cargo and Composer; a bare 1.2.3 is an exact version in npm but a caret range in Cargo; pip has no ^ at all and uses ~= (compatible release) from PEP 440.

Side by side

MeaningnpmCargoComposerpip (PEP 440)
exactly 1.2.31.2.3=1.2.31.2.3==1.2.3
>=1.2.3 <2.0.0^1.2.31.2.3 or ^1.2.3^1.2.3>=1.2.3,<2
>=1.2.3 <1.3.0~1.2.3~1.2.3~1.2.3~=1.2.3
>=1.2 <2.0^1.2 or 1.x1.2~1.2~=1.2
any 0.2.x (pre-1.0 caret)^0.2.3 → <0.3.00.2.3 → <0.3.0^0.2.3 → <0.3.0>=0.2.3,<0.3

For 0.x versions, the caret in npm, Cargo and Composer does not cross the minor version, because SemVer treats 0.y.z as unstable.

Check npm ranges with the semver package

bash
npm init -y >/dev/null
npm install --silent --no-audit --no-fund semver@7 >/dev/null 2>&1
node -e '
const semver = require("semver");
const versions = ["1.2.2", "1.2.3", "1.2.9", "1.3.0", "1.9.9", "2.0.0", "0.2.5", "0.3.0"];
for (const range of ["^1.2.3", "~1.2.3", "1.2.3", "^0.2.3"]) {
  console.log(range.padEnd(7), "->", versions.filter((v) => semver.satisfies(v, range)).join(" "));
}'

Output

^1.2.3  -> 1.2.3 1.2.9 1.3.0 1.9.9
~1.2.3  -> 1.2.3 1.2.9
1.2.3   -> 1.2.3
^0.2.3  -> 0.2.5

Pitfalls

  • npm 1.2.3 in package.json is exact, but npm install pkg writes ^1.2.3 by default.
  • Cargo "1.2.3" in Cargo.toml is a caret range, not an exact pin; use "=1.2.3" to pin.
  • pip ~=1.2 allows 1.3, 1.9 (anything >=1.2, ==1.*), while ~=1.2.0 stays within 1.2.*.

Sources