pypa/pip · error · IndexError

No such group

Error message

No such group

What it means

Raised by _PseudoMatch.group(arg) when a non-None, truthy argument is passed. _PseudoMatch is a synthetic match object built from a captured string (used by the bygroups callback when a sub-action needs a match-like object); it only ever holds one 'group', so requesting a named/numbered group raises IndexError('No such group'). This is internal to lexer token-stream processing.

Solutions

  1. Restructure the rule so capturing groups are on the outer regex (accessed by the bygroups callback) rather than on the inner pseudo-match.
  2. Avoid calling group(n>0) or group('name') inside a bygroups sub-action callback.
  3. Use a regex with proper capturing groups and let bygroups map them to token types.

Example fix

# before
bygroups(Name, lambda l, m, ctx: process(m.group(1)))
# after
bygroups(Name, lambda l, m, ctx: process(m.group()))  # use the full pseudo-match text
Defensive patterns

Strategy: validation

Validate before calling

null

Type guard

null

Try / catch

try:
    ...  # bygroups sub-action calling match.group(n)
except IndexError as e:
    if 'No such group' in str(e):
        # fall back to group(0)
        val = match.group()
    else:
        raise

Prevention

When it happens

Trigger: A custom lexer rule uses bygroups(...) with a sub-action that calls match.group(n) for n>0 on a _PseudoMatch, or calls match.group('name'). The pseudo match only supports group(0)/group(None).

Common situations: Writing a nested lexer action inside bygroups that tries to access subgroups of a substring match; a regex with no capturing groups used where the action expects them.

Related errors


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

Appendix: source

Thrown at src/pip/_vendor/pygments/lexer.py:377

class _PseudoMatch:
    """
    A pseudo match object constructed from a string.
    """

    def __init__(self, start, text):
        self._text = text
        self._start = start

    def start(self, arg=None):
        return self._start

    def end(self, arg=None):
        return self._start + len(self._text)

    def group(self, arg=None):
        if arg:
            raise IndexError('No such group')
        return self._text

    def groups(self):
        return (self._text,)

    def groupdict(self):
        return {}


def bygroups(*args):
    """
    Callback that yields multiple actions for each group in the match.
    """
    def callback(lexer, match, ctx=None):
        for i, action in enumerate(args):
            if action is None:
                continue
            elif type(action) is _TokenType:

View on GitHub (pinned to f399c37189)