pypa/pip · error · ValueError
invalid glob %r: recursive glob "**" must be used alone
Error message
invalid glob %r: recursive glob "**" must be used alone
What it means
Raised by iglob() when a glob pattern uses the recursive '**' token adjacent to other characters (matched by _CHECK_RECURSIVE_GLOB). distlib only supports '**' as a standalone path segment (e.g. 'src/**' or '**/test_*.py'); using it like 'a**b' or 'foo**bar' is rejected with ValueError. '**' must be used alone within its path component.
Source
Thrown at src/pip/_vendor/distlib/util.py:1438
break
result /= 1000.0
return '%d %sB/s' % (result, unit)
#
# Glob functionality
#
RICH_GLOB = re.compile(r'\{([^}]*)\}')
_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$')
def iglob(path_glob):
"""Extended globbing function that supports ** and {opt1,opt2,opt3}."""
if _CHECK_RECURSIVE_GLOB.search(path_glob):
msg = """invalid glob %r: recursive glob "**" must be used alone"""
raise ValueError(msg % path_glob)
if _CHECK_MISMATCH_SET.search(path_glob):
msg = """invalid glob %r: mismatching set marker '{' or '}'"""
raise ValueError(msg % path_glob)
return _iglob(path_glob)
def _iglob(path_glob):
rich_path_glob = RICH_GLOB.split(path_glob, 1)
if len(rich_path_glob) > 1:
assert len(rich_path_glob) == 3, rich_path_glob
prefix, set, suffix = rich_path_glob
for item in set.split(','):
for path in _iglob(''.join((prefix, item, suffix))):
yield path
else:
if '**' not in path_glob:
for item in std_iglob(path_glob):
yield itemView on GitHub (pinned to d7d0d0a394)
Solutions
- Isolate '**' as its own segment: 'src/**' or '**/tests/*.py'.
- Use '*' or a brace set '{a,b}' instead of '**' for fixed-width matching.
- Re-read the pattern at the reported location and split '**' onto its own path component.
Example fix
// before
list(iglob('src/***.py'))
// after
list(iglob('src/**/*.py')) Defensive patterns
Strategy: validation
Validate before calling
import re
_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
def safe_iglob(pattern):
if _CHECK_RECURSIVE_GLOB.search(pattern):
raise ValueError('recursive ** must be a standalone segment: %r' % pattern)
from distlib.util import iglob
return list(iglob(pattern)) Try / catch
from distlib.util import iglob
try:
matches = list(iglob(pattern))
except ValueError as e:
if 'recursive glob' in str(e):
pattern = pattern.replace('**', '/*/') # fall back to single-level
matches = list(iglob(pattern))
else:
raise Prevention
- Always place '**' as its own path segment between slashes or at an end.
- Validate glob patterns with _CHECK_RECURSIVE_GLOB before calling iglob().
- Avoid concatenating '**' with other characters.
When it happens
Trigger: iglob('src/***.py'), iglob('foo**bar'), iglob('a/**b'), or any pattern where '**' is directly concatenated with non-separator characters.
Common situations: Patterns imported from shells that allow looser '**', or programmatically-built globs that embed '**' inside a larger token.
Related errors
- invalid glob %r: mismatching set marker '{' or '}'
- {req_name} does not appear to be a Python project: neither '
- Directory {name!r} is not installable. Neither 'setup.py' no
- Invalid requirement: {req_as_string!r}: {exc}
- Invalid requirement: {req_string!r}: {exc}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d118eabdbe523889.json.
Report an issue: GitHub.