semver -c (--coerce): turn v2, 1.2 or 'nginx/1.27.4' into a valid version
- tool
- SemVer
- command
- semver
- flag
- -c, --coerce
- default
- off (strings that are not valid SemVer are rejected and semver exits with 1)
semver -c "v2" prints 2.0.0, semver -c "1.2" prints 1.2.0, and semver -c "nginx/1.27.4 (Ubuntu)" prints 1.27.4: coercion finds the first group of one to three dot-separated numbers in the input and turns it into a valid SemVer MAJOR.MINOR.PATCH, padding missing parts with zero. It is the way to compare versions reported by tools (python --version, openssl version, a Git tag) that are not strict SemVer. Without -c, semver accepts only valid versions and exits with 1 otherwise.
Coercing real-world version strings
node-semver's CLI is installed in a temporary folder (npx semver behaves the same):
npm init -y >/dev/null && npm install --silent --no-audit --no-fund semver@7.8.5 >/dev/null 2>&1
export PATH="$PWD/node_modules/.bin:$PATH"
for v in v2 "version 1.2" 1.2.3.4 "nginx/1.27.4 (Ubuntu)" "Python 3.13.15" "OpenSSL 3.5.7 9 Jun 2026" release-2024.10 1.2.3-beta.1; do
printf '%-28s -> %s\n' "$v" "$(semver -c "$v")"
done
echo "--- right to left:"; semver -c --rtl 1.2.3.4
echo "--- keep the prerelease with -p:"; semver -c -p "v1.2.3-beta.2+build"
echo "--- without -c:"; semver "version 1.2"; echo "exit $?"
echo "--- nothing to coerce:"; semver -c "latest"; echo "exit $?"
echo "--- coerce, then filter by range:"; semver -c 1.2.3 1.10 2 -r ">=1.5"Output
v2 -> 2.0.0 version 1.2 -> 1.2.0 1.2.3.4 -> 1.2.3 nginx/1.27.4 (Ubuntu) -> 1.27.4 Python 3.13.15 -> 3.13.15 OpenSSL 3.5.7 9 Jun 2026 -> 3.5.7 release-2024.10 -> 2024.10.0 1.2.3-beta.1 -> 1.2.3 --- right to left: 2.3.4 --- keep the prerelease with -p: 1.2.3-beta.2 --- without -c: exit 1 --- nothing to coerce: exit 1 --- coerce, then filter by range: 1.10.0 2.0.0
Text around the numbers is ignored, a fourth component is dropped, and a prerelease suffix is removed unless -p is given. A string with no digits at all cannot be coerced: semver prints nothing and exits with 1. Combined with -r, every argument is coerced first and then filtered by the range.
Rules of coercion
| Input | Result | Rule |
|---|---|---|
| v2 | 2.0.0 | Missing minor and patch become 0 |
| 1.2 | 1.2.0 | Same, with one number missing |
| 1.2.3.4 | 1.2.3 (2.3.4 with --rtl) | At most three numbers are used |
| nginx/1.27.4 (Ubuntu) | 1.27.4 | Leading and trailing text is ignored |
| 1.2.3-beta.1 | 1.2.3 (1.2.3-beta.1 with -p) | Prerelease dropped unless -p |
| latest | (nothing, exit 1) | No digits to use |
When to use it, and when not
Coercion is ideal for "is the installed tool new enough?" checks:
have=$(semver -c "$(openssl version)")
semver -r ">=3.0.0" "$have" >/dev/null || echo "OpenSSL 3 required, found $have"Do not use it to validate input that is supposed to be SemVer (package versions, release tags): coercion happily turns 1.2.3.4-hotfix or v2-final into something valid and hides the mistake. Validate strictly instead, as on validate a SemVer string, and handle the library's TypeError: Invalid Version.
Pitfalls
- Date-like versions.
release-2024.10becomes2024.10.0, which sorts correctly against other dates but is not a semantic version in spirit. - Leading zeros.
v01.02.03is not coerced in node-semver 7.8 (it prints nothing); strip the zeros first. - Shell comparison. For simple numeric comparisons without Node,
sort -Vis an alternative; see compare two versions in bash. For malformed but nearly valid versions, semver -l may be the better choice.
FAQ
Does --coerce keep the prerelease part?
Not by default: semver -c 1.2.3-beta.1 prints 1.2.3. Add -p (include prerelease), which in node-semver 7.8 keeps prerelease and build parts when coercing: v1.2.3-beta.2+build becomes 1.2.3-beta.2.
What does --rtl do?
It reads the string right to left, so in 1.2.3.4 the last three numbers are used (2.3.4) instead of the first three (1.2.3). This helps with version strings that carry a prefix number, such as a product generation.
Is coercion the same as loose parsing?
No. --loose accepts slightly malformed versions such as =v1.2.3 or 1.2.3beta as they are; --coerce extracts the first version-like number group from any text and fills in missing minor and patch numbers with 0.