pypa/pip · error · InvalidSpecifier
Invalid specifier: {spec!r}
Error message
Invalid specifier: {spec!r} What it means
Raised as InvalidSpecifier (specifiers.py:587), a ValueError subclass, by Specifier.__init__ when the input string does not fully match the operator+version regex (_regex.fullmatch). It is the primary PEP 440 specifier-syntax error, thrown before any operator/version splitting happens.
Source
Thrown at src/pip/_vendor/packaging/specifiers.py:587
">": "greater_than",
"===": "arbitrary",
}
def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
"""Initialize a Specifier instance.
:param spec:
The string representation of a specifier which will be parsed and
normalized before use.
:param prereleases:
This tells the specifier if it should accept prerelease versions if
applicable or not. The default of ``None`` will autodetect it from the
given specifiers.
:raises InvalidSpecifier:
If the given specifier is invalid (i.e. bad syntax).
"""
if not self._regex.fullmatch(spec):
raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
spec = spec.strip()
if spec.startswith("==="):
operator, version = spec[:3], spec[3:].strip()
elif spec.startswith(("~=", "==", "!=", "<=", ">=")):
operator, version = spec[:2], spec[2:].strip()
else:
operator, version = spec[:1], spec[1:].strip()
self._spec: tuple[str, str] = (operator, version)
# Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases
# Specifier version cache
self._spec_version: tuple[str, Version] | None = None
# Populated on first wildcard (==X.*) comparisonView on GitHub (pinned to d7d0d0a394)
Solutions
- Rewrite the specifier using a PEP 440 operator: ==, !=, <=, >=, <, >, ~=, or ===.
- Strip whitespace/unicode and ensure the version segment is a valid PEP 440 version (or arbitrary string after ===).
- If accepting user input, validate with Specifier() inside a try/except InvalidSpecifier and report a friendly message.
Example fix
# before
from pip._vendor.packaging.specifiers import Specifier
Specifier("^1.2.3") # npm-style, not PEP 440
Specifier("==1.0 beta")
# after
Specifier(">=1.2.3")
Specifier("==1.0") Defensive patterns
Strategy: try-catch
Validate before calling
from pip._vendor.packaging.specifiers import Specifier, InvalidSpecifier
def is_valid_specifier(s):
try:
Specifier(s)
return True
except InvalidSpecifier:
return False Type guard
null
Try / catch
from pip._vendor.packaging.specifiers import InvalidSpecifier
try:
spec = Specifier(user_input)
except InvalidSpecifier as e:
report_to_user(f'Not a valid PEP 440 specifier: {e}') Prevention
- Always wrap user-supplied specifier strings in try/except InvalidSpecifier.
- Reject npm/gem-style operators (^, ~>, ->) at the input boundary.
When it happens
Trigger: Specifier('==>1.0'), Specifier('1.0'), Specifier('==1.0 beta'), Specifier('~>1.0'), Specifier(''), or any specifier missing a recognized operator (== != <= >= < > ~= ===). Also reached transitively when SpecifierSet('bad spec') parses each comma-separated piece.
Common situations: User-typed requirement strings from a config file or CLI. Tools forwarding npm-style ('^1.0') or gem-style ('~>1.0') specifiers into packaging. A trailing space, unicode lookalike operator, or missing operator.
Related errors
- Ill-formed name/version string: '%s'
- epoch must be non-negative integer, got {epoch}
- release must be a non-empty tuple of non-negative integers,
- pre must be a tuple of ('a'|'b'|'rc', non-negative int), got
- post must be non-negative integer, got {value}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/15d6eef383c09b66.json.
Report an issue: GitHub.