pypa/pip · error · SyntaxError

unexpected trailing data: %s

Error message

unexpected trailing data: %s

What it means

Raised by parse_requirement() at the very end of parsing: after the name, versions, URL, and any environment marker have been consumed, if non-empty text remains whose first character is not '#' (comment), a SyntaxError 'unexpected trailing data' is raised. It is the final integrity check that the whole requirement string was consumed.

Source

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

                else:
                    m = VERSION_IDENTIFIER.match(s)
                    if not m:
                        raise SyntaxError('invalid constraint: %s' % s)
                    v = m.groups()[0]
                    s = s[m.end():].lstrip()
                    if s:
                        raise SyntaxError('invalid constraint: %s' % s)
                    versions = [('~=', v)]

    if remaining:
        if remaining[0] != ';':
            raise SyntaxError('invalid requirement: %s' % remaining)
        remaining = remaining[1:].lstrip()

        mark_expr, remaining = parse_marker(remaining)

    if remaining and remaining[0] != '#':
        raise SyntaxError('unexpected trailing data: %s' % remaining)

    if not versions:
        rs = distname
    else:
        rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions]))
    return Container(name=distname, extras=extras, constraints=versions, marker=mark_expr, url=uri, requirement=rs)


def get_resources_dests(resources_root, rules):
    """Find destinations for resources files"""

    def get_rel_path(root, path):
        # normalizes and returns a lstripped-/-separated path
        root = root.replace(os.path.sep, '/')
        path = path.replace(os.path.sep, '/')
        assert path.startswith(root)
        return path[len(root):].lstrip('/')

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Examine the trailing data reported and remove it.
  2. If the trailing data is part of a marker, fix the marker syntax (balance quotes and parentheses) so the whole marker is consumed.
  3. Split compound lines into one requirement per line.

Example fix

// before
parse_requirement('pkg >= 1.0 ; python_version >= "3.8")')
// after
parse_requirement('pkg >= 1.0 ; python_version >= "3.8"')
Defensive patterns

Strategy: try-catch

Try / catch

from distlib.util import parse_requirement
try:
    spec = parse_requirement(raw)
except SyntaxError as e:
    if 'unexpected trailing data' in str(e):
        log_trailing(raw, e)
    else:
        raise

Prevention

When it happens

Trigger: parse_requirement('pkg >= 1.0 ; python_version >= "3.8" extra'), parse_requirement('pkg ; python_version >= "3" )'), or any requirement where characters survive after the marker expression is parsed.

Common situations: Malformed marker expressions that terminate early (e.g. unbalanced quotes/parens inside the marker), or stray characters appended after a valid requirement.

Related errors


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