Safeguard
Security Guides•

Why do Python and SemVer versions sort differently in scanners?

Pre-releases, post-releases and epochs follow different rules in Python and SemVer. A scanner that mixes them can put a version on the wrong side of a fix.

Safeguard Security Team
3 min read

Python and SemVer both order versions, but with different rules. Python has post-releases and epochs, SemVer has hyphenated pre-release labels, and each ignores syntax the other uses. A scanner that compares every version with one rule can call a patched build vulnerable, or the reverse.

What each standard says

PEP 440 defines Python's public version as an optional epoch, a release number, an optional pre-release, post-release and development release. Within one release the order is development, then alpha, beta and release candidate, then the plain release, then post-releases. In its words, 1.0.dev1 sorts before 1.0a1, which sorts before 1.0, which sorts before 1.0.post1.

Epochs are written like 1!1.0. Each epoch sorts after the one before it, and without an explicit epoch the implicit value is 0. Local versions use a plus sign, such as 1.0+abc.5, and PyPI does not allow them.

SemVer 2.0.0 uses a hyphen for pre-releases. Its example order is 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-beta, 1.0.0-beta.2, 1.0.0-beta.11, 1.0.0-rc.1, then 1.0.0. Numeric identifiers compare as numbers, so beta.2 is lower than beta.11. Build metadata is ignored for precedence.

Where they disagree

FeaturePython (PEP 440)SemVer 2.0.0
Pre-release markera, b, rc suffixHyphen plus identifiers
Post-releaseSupported, sorts after the releaseNot defined
EpochSupportedNot defined
Build or local partPlus sign local labelPlus sign build metadata, ignored

PEP 440 says the major, minor and patch parts of SemVer are compatible with its scheme, and that SemVer pre-release and build labels are not allowed in a public Python version.

What goes wrong in a scan

  • A fix released as 2.3.post1 is later than 2.3. A tool using string or SemVer rules may rank it incorrectly.
  • A version with an epoch sorts above everything without one. Ignoring the epoch can invert the result.
  • A pre-release of the fixed version, such as a release candidate, falls before the fix. Whether it is vulnerable depends on the advisory.

OSV addresses this by letting a record declare the range type. ECOSYSTEM ranges follow the package ecosystem's ordering, SEMVER ranges follow SemVer. See how OSV version ranges work.

A boundary test

  1. Choose a Python package with a known fix version.
  2. Create files pinning the version below the fix, the fix itself, and a post-release or release candidate near it if one exists.
  3. Scan each and check that only the versions the advisory lists are flagged.

Safeguard's dependency scanning covers many ecosystems, and the free scan at https://gold.safeguard.sh/scan lets you try boundary versions yourself.

Quick check

Is 1.0.post1 newer than 1.0 in Python?

Yes. PEP 440 orders post-releases immediately after the matching release.

Does SemVer ignore build metadata when comparing?

Yes. Versions that differ only in build metadata have the same precedence.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.