pypa/pip · error · ParserSyntaxError

Expected extra name after comma

Error message

Expected extra name after comma

What it means

Raised as a ParserSyntaxError (surfacing as InvalidRequirement) at line 245 inside _parse_extras_list: after consuming a comma in an extras list (line 242), tokenizer.expect("IDENTIFIER", expected="extra name after comma") fails because no valid identifier follows the comma. The grammar (line 226) requires an identifier after each comma.

Solutions

  1. Remove the trailing/empty comma: 'pkg[a,b]' instead of 'pkg[a,b,]'.
  2. When building extras programmatically, filter empties and avoid a trailing comma: '[' + ','.join(filter(None, extras)) + ']'.
  3. Validate the extras segment parses before using it.

Example fix

// before
Requirement('pkg[dev,test,]')

// after
Requirement('pkg[dev,test]')
Defensive patterns

Strategy: validation

Validate before calling

def clean_extras(extras):
    return [e for e in (x.strip() for x in extras) if e]
def extras_str(extras):
    return '[' + ','.join(clean_extras(extras)) + ']' if clean_extras(extras) else ''

Try / catch

from packaging.requirements import Requirement, InvalidRequirement
try:
    req = Requirement(raw)
except InvalidRequirement as e:
    if 'extra name after comma' in str(e):
        # strip trailing/empty commas in the extras bracket
        raw = re.sub(r',+', ',', raw)
    raise

Prevention

When it happens

Trigger: A requirement with a trailing comma in extras, e.g. 'pkg[a,]', or an empty slot like 'pkg[a,,b]', or a non-identifier token after the comma, e.g. 'pkg[a,2]'. After tokenizer.read() consumes the comma (line 242), the next token is not an IDENTIFIER.

Common situations: Programmatically joining extras with a trailing comma (','.join(extras) + ','); user-edited extras strings with a stray comma; copy-paste leaving a dangling comma.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/7339376c9970e3ad. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_vendor/packaging/_parser.py:245

    """
    extras: list[str] = []

    if not tokenizer.check("IDENTIFIER"):
        return extras

    extras.append(tokenizer.read().text)

    while True:
        tokenizer.consume("WS")
        if tokenizer.check("IDENTIFIER", peek=True):
            tokenizer.raise_syntax_error("Expected comma between extra names")
        elif not tokenizer.check("COMMA"):
            break

        tokenizer.read()
        tokenizer.consume("WS")

        extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma")
        extras.append(extra_token.text)

    return extras


def _parse_specifier(tokenizer: Tokenizer) -> str:
    """
    specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS
              | WS? version_many WS?
    """
    with tokenizer.enclosing_tokens(
        "LEFT_PARENTHESIS",
        "RIGHT_PARENTHESIS",
        around="version specifier",
    ):
        tokenizer.consume("WS")
        parsed_specifiers = _parse_version_many(tokenizer)
        tokenizer.consume("WS")

View on GitHub (pinned to f399c37189)