pypa/pip · error · SyntaxError

comma expected in extras: %s

Error message

comma expected in extras: %s

What it means

Raised by distlib's `parse_requirement` while parsing a multi-value extras list: after consuming one extra, the next character must be `,` to separate further extras. If it isn't (e.g. a stray letter or punctuation), the parser raises `SyntaxError: comma expected in extras`.

Source

Thrown at src/pip/_vendor/distlib/util.py:174

    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)
            # there are issues with Python and URL parsing, so this test
            # is a bit crude. See bpo-20271, bpo-23505. Python doesn't
            # always parse invalid URLs correctly - it should raise
            # exceptions for malformed URLs
            if not (t.scheme and t.netloc):
                raise SyntaxError('Invalid URL: %s' % uri)

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Separate multiple extras with commas: `pkg[extra1,extra2]`.
  2. Remove whitespace/punctuation that isn't a comma between extras.
  3. Validate with `packaging.requirements.Requirement(...)`.

Example fix

# before
requests[security socks]

# after
requests[security,socks]
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
def extras_use_commas(line: str) -> bool:
    m = re.search(r"\[([^\]]*)\]", line)
    if not m:
        return True
    inner = m.group(1).strip()
    if not inner:
        return True
    # multiple extras must be comma-separated
    parts = inner.split(",")
    return all(re.match(r"^[A-Za-z_][\\w.-]*$", p.strip()) for p in parts)

Try / catch

null

Prevention

When it happens

Trigger: Parsing a requirement like `pkg[extra1 extra2]` (space instead of comma) or `pkg[extra1;extra2]`. Triggered when the extras list contains more than one entry without comma separators.

Common situations: Using spaces, semicolons, or other delimiters instead of commas between extras; typos; copy-paste from sources that used a different separator.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/1f0fcbc721888f2e.json. Report an issue: GitHub.