nodejs/node · error · UndefinedComparison

Undefined {op!r} on {lhs!r} and {rhs!r}.

Error message

Undefined {op!r} on {lhs!r} and {rhs!r}.

What it means

Raised by _eval_op (the core of packaging's environment-marker evaluation) when an operator token is neither a valid PEP 440 specifier prefix (for Specifier-based comparison) nor a key in the built-in _operators dict. PEP 508 environment markers like 'python_version >= "3.8"' are evaluated here; an unrecognized operator means the marker expression contains an operator the library cannot interpret.

Source

Thrown at tools/gyp/pylib/packaging/markers.py:120

    "<=": operator.le,
    "==": operator.eq,
    "!=": operator.ne,
    ">=": operator.ge,
    ">": operator.gt,
}


def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
    try:
        spec = Specifier("".join([op.serialize(), rhs]))
    except InvalidSpecifier:
        pass
    else:
        return spec.contains(lhs, prereleases=True)

    oper: Optional[Operator] = _operators.get(op.serialize())
    if oper is None:
        raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")

    return oper(lhs, rhs)


def _normalize(*values: str, key: str) -> Tuple[str, ...]:
    # PEP 685 – Comparison of extra names for optional distribution dependencies
    # https://peps.python.org/pep-0685/
    # > When comparing extra names, tools MUST normalize the names being
    # > compared using the semantics outlined in PEP 503 for names
    if key == "extra":
        return tuple(canonicalize_name(v) for v in values)

    # other environment markers don't have such standards
    return values


def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
    groups: List[List[bool]] = [[]]

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Inspect the full marker string that triggered the error and locate the invalid operator.
  2. Correct the operator to a valid PEP 508 operator: ==, !=, <=, >=, <, >, ~=, in, not in.
  3. Upgrade the `packaging` library to a version that supports the marker syntax you are using.
  4. Report the malformed marker to the package maintainer if it comes from third-party metadata you do not control.

Example fix

# before (malformed marker in metadata)
os_name ~=='posix'
# after
os_name == 'posix'
Defensive patterns

Strategy: try-catch

Validate before calling

# Validate marker operators before evaluation
from packaging.markers import Marker
_VALID_OPS = {'==', '!=', '<=', '>=', '<', '>', '~=', 'in', 'not in'}
# If constructing markers programmatically, ensure ops are in _VALID_OPS

Type guard

def is_valid_marker(text: str) -> bool:
    from packaging.markers import Marker, InvalidMarker
    try:
        Marker(text)
        return True
    except InvalidMarker:
        return False

Try / catch

from packaging.markers import UndefinedComparison
try:
    result = Marker(text).evaluate()
except UndefinedComparison as e:
    print(f'unsupported operator in marker: {e}')
    result = True  # safe default

Prevention

When it happens

Trigger: A Marker expression is evaluated whose AST contains an Op that serializes to a string not in _operators (which covers in, not in, <, <=, ==, !=, >=, >) and not parseable as a specifier prefix (==, !=, <=, >=, <, >, ~=). This generally means the marker was parsed from malformed or non-standard PEP 508 text.

Common situations: A third-party package's METADATA contains a malformed environment marker with a typo'd or non-standard operator. A packaging library version mismatch where a newer marker syntax is fed to an older parser. Programmatically constructing an invalid marker expression.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/f9ec9a0e8686063b. Report an issue: GitHub.