pypa/pip · error · SyntaxError
error in string literal: %s
Error message
error in string literal: %s
What it means
Raised inside distlib's PEP 508 marker parser (`parse_marker` -> `marker_var`) while consuming a quoted string literal within an environment marker expression. If the next character is not a valid string chunk per the `STRING_CHUNK` regex (e.g. contains a character not allowed in the chunk class), the parser aborts with `SyntaxError: error in string literal`. This is a vendored copy of distlib used by pip for parsing requirement markers like `; python_version >= '3.8'`.
Source
Thrown at src/pip/_vendor/distlib/util.py:89
raise SyntaxError('unexpected end of input')
else:
q = remaining[0]
if q not in '\'"':
raise SyntaxError('invalid expression: %s' % remaining)
oq = '\'"'.replace(q, '')
remaining = remaining[1:]
parts = [q]
while remaining:
# either a string chunk, or oq, or q to terminate
if remaining[0] == q:
break
elif remaining[0] == oq:
parts.append(oq)
remaining = remaining[1:]
else:
m = STRING_CHUNK.match(remaining)
if not m:
raise SyntaxError('error in string literal: %s' % remaining)
parts.append(m.groups()[0])
remaining = remaining[m.end():]
else:
s = ''.join(parts)
raise SyntaxError('unterminated string: %s' % s)
parts.append(q)
result = ''.join(parts)
remaining = remaining[1:].lstrip() # skip past closing quote
return result, remaining
def marker_expr(remaining):
if remaining and remaining[0] == '(':
result, remaining = marker(remaining[1:].lstrip())
if remaining[0] != ')':
raise SyntaxError('unterminated parenthesis: %s' % remaining)
remaining = remaining[1:].lstrip()
else:
lhs, remaining = marker_var(remaining)View on GitHub (pinned to d7d0d0a394)
Solutions
- Quote marker string literals with single or double quotes and avoid disallowed characters inside them (notably backslashes and characters outside the chunk class).
- Use plain ASCII variable names and values in markers (`python_version`, `sys_platform`, etc.).
- Validate the requirement string with `packaging.requirements.Requirement(...)` before passing it to pip.
Example fix
# before requests==2.28.0 ; python_version >= "3\.8" # backslash inside literal # after requests==2.28.0 ; python_version >= "3.8"
Defensive patterns
Strategy: validation
Validate before calling
from packaging.markers import Marker, InvalidMarker
def validate_marker(req_with_marker: str) -> None:
# split off the marker after ';'
if ";" in req_with_marker:
marker = req_with_marker.split(";", 1)[1].strip()
try:
Marker(marker)
except InvalidMarker as e:
raise ValueError(f"Bad marker literal: {e}") from e Type guard
from packaging.markers import Marker, InvalidMarker
def is_valid_marker(m: str) -> bool:
try:
Marker(m)
return True
except InvalidMarker:
return False Try / catch
null
Prevention
- Validate markers with `packaging.markers.Marker(...)` before pip sees them.
- Use ASCII, plain quotes (`'...'`), and avoid backslashes in marker literals.
- Lint requirements files for non-ASCII smart quotes.
When it happens
Trigger: `parse_marker(marker_string)` is called (transitively when pip parses a requirement with a marker) and a quoted literal in the marker contains a character outside the `STRING_CHUNK` allowed set `[^\s\w.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]` (e.g. a backslash, an emoji, or an unmatched quote inside the string).
Common situations: Typos in requirement markers in `requirements.txt`; copy-pasting markers from documentation that use smart quotes; embedding invalid characters; a stray `\` or unescaped quote inside the marker string literal.
Related errors
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/dfc1bb13b528e477.json.
Report an issue: GitHub.