nodejs/node · error · UndefinedComparison

Undefined on and .

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.

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.

Appendix: 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)