oraios/serena · error

Regex must contain exactly one group to capture the position

Error message

Regex must contain exactly one group to capture the position, but found {len(match.groups())} groups.

What it means

find_text_coordinates() returns the position of the regex's captured group; the pattern must therefore contain exactly one capture group. Zero groups (nothing to report position of) or 2+ groups make the intended coordinate undefined, so it raises.

Source

Thrown at src/serena/util/text_utils.py:673

    :param content: the text content to search through
    :param regex: the regular expression pattern to search for; it must match part of a single line,
        and contain exactly one group that captures the position of interest (e.g., the exact variable name to find the coordinates of)
    :param require_unique: if True, raises an error if not exactly one match is found;
        if False, returns None if no match is found, and returns the coordinates of the first match if multiple matches are found
    :return: the coordinates of the match or None
    """
    pattern = re.compile(regex, flags=re.MULTILINE | re.DOTALL)
    matches = list(pattern.finditer(content))
    if len(matches) == 0:
        if require_unique:
            raise ValueError(f"No match found for regex: {regex}")
        return None
    else:
        if require_unique and len(matches) > 1:
            raise ValueError(f"Match must be unique; found {len(matches)} matches for regex: {regex}")
        match = matches[0]
        if len(match.groups()) != 1:
            raise ValueError(f"Regex must contain exactly one group to capture the position, but found {len(match.groups())} groups.")
        index_in_content = match.start(1)
        line, col = TextUtils.get_line_col_from_index(content, index_in_content)
        return TextCoords(line, col)

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Wrap the token whose position you want in exactly one group: r'... (target) ...'
  2. Convert auxiliary grouping parentheses to non-capturing (?:...)
  3. Count groups with re.compile(pattern).groups before calling

Example fix

// before
find_text_coordinates(content, r'def (my_func)(args)')  # 2 groups
// after
find_text_coordinates(content, r'def (my_func)(?:args)')  # exactly 1 group
Defensive patterns

Strategy: validation

Validate before calling

g = re.compile(regex).groups
if g != 1:
    raise ValueError(f'locator regex needs exactly one capture group, has {g}')

Type guard

def has_single_group(regex: str) -> bool:
    return re.compile(regex).groups == 1

Try / catch

try:
    coords = find_text_coordinates(content, regex)
except ValueError as e:
    if 'exactly one group' in str(e):
        coords = find_text_coordinates(content, f'({regex})')  # wrap single group
    else:
        raise

Prevention

When it happens

Trigger: Passing a regex with no parentheses, or with multiple capture groups like r'(def) (foo)', to find_text_coordinates.

Common situations: Reusing a search-only regex as a locator without adding a capturing group; adding non-capturing context as parentheses instead of (?:...).

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/d58368a8e602bebb. Report an issue: GitHub.