Skip to content

Why does >1.2 throw as an invalid comparator? #802

Open

Description

@jsumners

['>1.2', '1.2.8'],

I am trying to understand why npx -r '>1.2' 1.2.8 results in 1.2.8 not satisfying the range. I think >1.2 should expand to >1.2.0 <1.3.0 which would mean 1.2.8 satisfies. But stepping through the tool, I see it hits this path:

const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
const m = comp.match(r)
if (!m) {
throw new TypeError(`Invalid comparator: ${comp}`)
}

The result being m === null and the TypeError is thrown.

Activity

  1. ljharb commented on Aug 27, 2025

    @ljharb
    Contributor

    >1.2 does not expand to >1.2.0, it equates to >= 1.3. In other words, > 1.2 means that no 1.2.x version can satisfy it.

  2. jsumners commented on Aug 28, 2025

    @jsumners
    Author

    I see. Thank you.

  3. jsumners commented on Aug 28, 2025

    @jsumners
    Author

    Sorry, I'm reopening this. I think this line is confirming your answer @ljharb:

    node-semver/README.md

    Lines 164 to 166 in d17aebf

    or `1.1.0`. The comparator `>1` is equivalent to `>=2.0.0` and
    would match the versions `2.0.0` and `3.1.0`, but not the versions
    `1.0.1` or `1.1.0`.

    But further down we have:

    node-semver/README.md

    Lines 316 to 321 in d17aebf

    A partial version range is treated as an X-Range, so the special
    character is in fact optional.
    * `""` (empty string) := `*` := `>=0.0.0`
    * `1` := `1.x.x` := `>=1.0.0 <2.0.0-0`
    * `1.2` := `1.2.x` := `>=1.2.0 <1.3.0-0`

    This seems to be a conflict to me.

  4. wraithgar commented on Aug 28, 2025

    @wraithgar
    Contributor

    1.2 is not the same as >1.2

  5. jsumners commented on Aug 28, 2025

    @jsumners
    Author

    1.2 is a component of >1.2. The version string should still expand the same.

  6. ljharb commented on Aug 28, 2025

    @ljharb
    Contributor

    1.2 does not expand to 1.2.0. It expands to 1.2.x, and >1.2.x indeed excludes any 1.2.

  7. jsumners commented on Aug 28, 2025

    @jsumners
    Author

    It expands to 1.2.x

    Which expands to 1.2.0. Thus, >1.2 should expand to >1.2.0, right?

  8. ljharb commented on Aug 28, 2025

    @ljharb
    Contributor

    No, it does not expand to 1.2.0. It can't expand without a concrete list of existing versions, and it will always match the latest 1.2, not the .0

  9. jsumners commented on Aug 28, 2025

    @jsumners
    Author

    I'm just not seeing the rule you're basing that on. If the readme isn't the source of truth for the expansion rules, please point me to the right document.

  10. ljharb commented on Aug 28, 2025

    @ljharb
    Contributor

    It’s not documented that i can see, but it’s common sense - would “1.2” be satisfied by 1.2.99? Obviously yes, because it’s a range, not a short way to express one version.

  11. jsumners commented on Aug 29, 2025

    @jsumners
    Author

    Sorry, you've just confused me even more. I originally asserted that 1.2.8 should satisfy >1.2. Your response was that it shouldn't. Now you're suggesting it should.

  12. ljharb commented on Aug 30, 2025

    @ljharb
    Contributor

    1.2.8 certainly satisfies 1.2. It certainly does not satisfy >1.2 (which is the same as >= 1.3).

  13. aMarCruz commented on Sep 1, 2025

    @aMarCruz

    This discussion will continue for years until there's an npm specification :)

    @jsumners ,
    As you, I'm surprised me because I thought semver.coerce() was being applied before the comparison.
    Then I reasoned that it was checking for inclusion in a set, so the logical thing to do was to convert '1.2' to '1.2.x' and not coerce to '1.2.0' (the same for 1 to 1.x.x).
    After that, the results in this list made perfect sense to me...

    semver.satisfies('1.2.0', '1')        // true -- '1' is converted to '1.x.x'
    semver.satisfies('1.2.0', '1.2')      // true -- '1.2' to '1.2.x' an so on
    semver.satisfies('1.2.0', '1.2.0')    // true
    semver.satisfies('1.2.0', '=1.2')     // true -- The '=' makes no difference
    semver.satisfies('1.2.0', '=1.2.0')   // true
    semver.satisfies('1.2.0', '~1.2')     // true -- '~#.#' is equal to '#.#.x'
    semver.satisfies('1.2.0', '~1.2.0')   // true
    semver.satisfies('1.2.0', '<1.2')     // false
    semver.satisfies('1.2.0', '<1.2.0')   // false
    semver.satisfies('1.2.0', '<=1.2')    // true
    semver.satisfies('1.2.0', '<=1.2.0')  // true
    semver.satisfies('1.2.0', '>1.2')     // false
    semver.satisfies('1.2.0', '>1.2.0')   // false
    semver.satisfies('1.2.0', '>=1.2')    // true
    semver.satisfies('1.2.0', '>=1.2.0')  // true
    semver.satisfies('1.2.0', '^1.2')     // true
    semver.satisfies('1.2.0', '^1.2.0')   // true -- '^#.#' is equal to '#.x.x'

    ...as this...

    semver.satisfies('1.2.8', '1')        // true
    semver.satisfies('1.2.8', '1.2')      // true
    semver.satisfies('1.2.8', '1.2.0')    // false
    semver.satisfies('1.2.8', '=1.2')     // true
    semver.satisfies('1.2.8', '=1.2.0')   // false
    semver.satisfies('1.2.8', '~1.2')     // true
    semver.satisfies('1.2.8', '~1.2.0')   // true
    semver.satisfies('1.2.8', '<1.2')     // false
    semver.satisfies('1.2.8', '<1.2.0')   // false
    semver.satisfies('1.2.8', '<=1.2')    // true
    semver.satisfies('1.2.8', '<=1.2.0')  // false
    semver.satisfies('1.2.8', '>1.2')     // false
    semver.satisfies('1.2.8', '>1.2.0')   // true
    semver.satisfies('1.2.8', '>=1.2')    // true
    semver.satisfies('1.2.8', '>=1.2.0')  // true
    semver.satisfies('1.2.8', '^1.2')     // true
    semver.satisfies('1.2.8', '^1.2.0')   // true

    satisfies does not work as other semver methods ...gt, lt, etc.
    The conclusión is to use the full semver (n.n.n) if you're unsure.

    This is the gist with the source

  14. jsumners commented on Sep 1, 2025

    @jsumners
    Author

    @ljharb does this list of rules look correct to you?

    1. A version string with all three primary positions provided specifies an exact version, e.g. 1.2.3 is equivalent to =1.2.3.
    2. A version string with only one or two primary positions provided equates to an X-range, e.g 1.2 is equivalent to 1.2.x is equivalent to >=1.2.0 <1.3.0 and 1 is equivalent to 1.x.x is equivalent to >=1.0.0 <2.0.0.
    3. A version string prefixed with any primitive (< | > | >= | <= | =) is not an X-range and only the positions provided count, e.g >1.2.3 is equivalent to >=1.2.4, >1.2 is equivalent to >=1.3.0, and >1 is equivalent to >=2.0.0.
    4. Tilde and Caret range expansions are described fully in the readme.
  15. ljharb commented on Sep 1, 2025

    @ljharb
    Contributor

    There's some nuance you're missing about prereleases, but otherwise that sounds correct.

  16. aMarCruz commented on Sep 2, 2025

    @aMarCruz
    1. A version string with all three primary positions provided specifies an exact version, e.g. 1.2.3 is equivalent to =1.2.3.

    yes

    1. A version string with only one or two primary positions provided equates to an X-range, e.g 1.2 is equivalent to 1.2.x is equivalent to >=1.2.0 <1.3.0 and 1 is equivalent to 1.x.x is equivalent to >=1.0.0 <2.0.0.

    right

    1. A version string prefixed with any primitive (< | > | >= | <= | =) is not an X-range and only the positions provided count, e.g >1.2.3 is equivalent to >=1.2.4, >1.2 is equivalent to >=1.3.0, and >1 is equivalent to >=2.0.0.

    In the context of semver.satisfy() all the shortened versions are X-ranges.
    That's why it's better to use the full version with comparison operators, as in this example from eslint's package.json.

    {
      "engines": {
        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
      }
    }

    See the output of semver.toComparators('^18.18.0 || ^20.9.0 || >=21.1.0') ...

    [
      [ '>=18.18.0', '<19.0.0-0' ],
      [ '>=20.9.0', '<21.0.0-0' ],
      [ '>=21.1.0' ]
    ]
    

    Saludos desde México.

  17. ljharb commented on Sep 2, 2025

    @ljharb
    Contributor

    Indeed, ^18.8 and ^18.8.0 are identical. (typically the shorter one is preferred)

  18. jsumners commented on Sep 2, 2025

    @jsumners
    Author

    @aMarCruz thank you for the information. I am not concerned with the various methods exported by this module. I am solely concerned with how strings are meant to be parsed and interpreted. In that regard, your example for semver.satisfy is covered by the fourth "rule," not the third one.

  19. mbtools commented on Jun 20, 2026

    @mbtools
    Contributor

    >1.2 means "greater than any 1.2 version". Therefore, 1.2.8 does not satisfy the range.

    Works as designed. Let's close it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions