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 item

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Isolate '**' as its own segment: 'src/**' or '**/tests/*.py'.
  2. Use '*' or a brace set '{a,b}' instead of '**' for fixed-width matching.
  3. 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

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


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