pypa/pip · error · UndefinedComparison
Set-valued marker {environment_key!r} can only be used with
Error message
Set-valued marker {environment_key!r} can only be used with the membership form (e.g. "<name>" in {environment_key}); it cannot appear on the left-hand side of {op.serialize()!r}. What it means
Raised by _evaluate_markers in markers.py:311-317. The set-valued markers 'extras' and 'dependency_groups' (MARKERS_ALLOWING_SET) are stored as frozensets in the evaluation environment and may only be the right-hand operand of a membership test ('<value>' in extras). If such a variable appears on the left of any comparison, its value is a non-str set and UndefinedComparison is raised.
Source
Thrown at src/pip/_vendor/packaging/markers.py:312
groups: list[list[bool]] = [[]]
for marker in markers:
if isinstance(marker, list):
groups[-1].append(_evaluate_markers(marker, environment))
elif isinstance(marker, tuple):
lhs, op, rhs = marker
if isinstance(lhs, Variable):
environment_key = lhs.value
lhs_value = _lookup_environment(environment, environment_key)
rhs_value = rhs.value
else:
lhs_value = lhs.value
environment_key = rhs.value
rhs_value = _lookup_environment(environment, environment_key)
if not isinstance(lhs_value, str):
raise UndefinedComparison(
f"Set-valued marker {environment_key!r} can only be used "
f'with the membership form (e.g. "<name>" in '
f"{environment_key}); it cannot appear on the left-hand "
f"side of {op.serialize()!r}."
)
lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key))
elif marker == "or":
groups.append([])
elif marker == "and":
pass
else: # pragma: nocover
raise TypeError(f"Unexpected marker {marker!r}")
return any(all(item) for item in groups)
def _format_full_version(info: sys._version_info) -> str:View on GitHub (pinned to f399c37189)
Solutions
- Rewrite as a membership test with the literal on the left: '"test" in extras'.
- For negation use '"test" not in extras'.
Example fix
# before
Marker('extras == "test"')
# after
Marker('"test" in extras') Defensive patterns
Strategy: validation
Validate before calling
from packaging.markers import MARKERS_ALLOWING_SET
def membership_form_for_set_marker(marker_str: str) -> bool:
low = marker_str.lower()
for key in MARKERS_ALLOWING_SET:
# the set-valued variable must be the RIGHT operand of 'in'/'not in'
if key in low and f"{key} ==" in low or f"{key}!=" in low or f"{key}<" in low or f"{key}>" in low:
return False
return True
Try / catch
from packaging.markers import Marker, UndefinedComparison
try:
Marker(expr).evaluate()
except UndefinedComparison as e:
# rewrite extras/dependency_groups as membership form
...
Prevention
- Always write '"value" in extras' / '"value" in dependency_groups' (literal on the left).
- Never put extras or dependency_groups on the left of a comparison.
When it happens
Trigger: Evaluating Marker('extras == "test"'), Marker('dependency_groups != "dev"'), or 'extras in something' — i.e. extras/dependency_groups used as the left operand instead of the membership form. Triggers once the environment populates them as sets (context 'lock_file' or 'requirement').
Common situations: Writing extras markers like ordinary equality comparisons; following pre-PEP-685 examples; tools auto-generating markers in the wrong direction.
Related errors
- [dependency-groups] resolution failed for '{groupname}' from
- [dependency-groups] table was missing from '{path}'. Cannot
- [dependency-groups] table was malformed in {path}. Cannot re
- [dependency-groups] data was invalid in {path}: {'; '.join(m
- {path} not found. Cannot resolve '--group' option.
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/92d2cc47443f41fe.
Report an issue: GitHub.