pypa/pip · error · SyntaxError
malformed extra: %s
Error message
malformed extra: %s
What it means
Raised by distlib's `parse_requirement` while parsing the contents of the extras list: each item must match the `IDENTIFIER` regex; if the first token inside `[...]` is not a valid identifier (e.g. starts with a digit or punctuation), the parser raises `SyntaxError: malformed extra`.
Source
Thrown at src/pip/_vendor/distlib/util.py:168
if not remaining or remaining.startswith('#'):
return None
m = IDENTIFIER.match(remaining)
if not m:
raise SyntaxError('name expected: %s' % remaining)
distname = m.groups()[0]
remaining = remaining[m.end():]
extras = mark_expr = versions = uri = None
if remaining and remaining[0] == '[':
i = remaining.find(']', 1)
if i < 0:
raise SyntaxError('unterminated extra: %s' % remaining)
s = remaining[1:i]
remaining = remaining[i + 1:].lstrip()
extras = []
while s:
m = IDENTIFIER.match(s)
if not m:
raise SyntaxError('malformed extra: %s' % s)
extras.append(m.groups()[0])
s = s[m.end():]
if not s:
break
if s[0] != ',':
raise SyntaxError('comma expected in extras: %s' % s)
s = s[1:].lstrip()
if not extras:
extras = None
if remaining:
if remaining[0] == '@':
# it's a URI
remaining = remaining[1:].lstrip()
m = NON_SPACE.match(remaining)
if not m:
raise SyntaxError('invalid URI: %s' % remaining)
uri = m.groups()[0]
t = urlparse(uri)View on GitHub (pinned to d7d0d0a394)
Solutions
- Use valid Python-identifier-like extra names (start with letter/underscore, then `[A-Za-z0-9_.-]`).
- Cross-check the extra name against the package's declared extras (`pip show <pkg>` or the package docs).
- Remove extras you don't actually need.
Example fix
# before requests[2security] # after requests[security]
Defensive patterns
Strategy: validation
Validate before calling
from packaging.requirements import Requirement, InvalidRequirement
def validate_requirement(line: str) -> None:
try:
Requirement(line)
except InvalidRequirement as e:
raise ValueError(f"Bad requirement: {e}") from e Type guard
import re
_EXTRA = re.compile(r"^[A-Za-z_][\\w.-]*$")
def all_extras_valid(line: str) -> bool:
import re as _r
m = _r.search(r"\[([^\]]*)\]", line)
if not m:
return True
parts = [p.strip() for p in m.group(1).split(",") if p.strip()]
return all(bool(_EXTRA.match(p)) for p in parts) Try / catch
null
Prevention
- Use identifier-like extra names (start with letter/underscore).
- Cross-check extras against the package's declared extras list.
- Validate requirements with `packaging.requirements.Requirement`.
When it happens
Trigger: Parsing a requirement like `pkg[1invalid]` or `pkg[+foo]` where an extras entry doesn't start with a letter/underscore. Triggered when pip parses the extras section of a requirement string.
Common situations: Typos in extras names; invalid characters inside extras; copy-paste of wrong content; tooling generating malformed extras lists.
Related errors
- unterminated extra: %s
- comma expected in extras: %s
- error in string literal: %s
- unterminated string: %s
- unterminated parenthesis: %s
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/70f3859428e3716a.json.
Report an issue: GitHub.