{"record":{"id":"d58368a8e602bebb","repo":"oraios/serena","slug":"regex-must-contain-exactly-one-group-to-capture-th","errorCode":null,"errorMessage":"Regex must contain exactly one group to capture the position, but found {len(match.groups())} groups.","messagePattern":"Regex must contain exactly one group to capture the position, but found (.+?) groups\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/serena/util/text_utils.py","lineNumber":673,"sourceCode":"    :param content: the text content to search through\n    :param regex: the regular expression pattern to search for; it must match part of a single line,\n        and contain exactly one group that captures the position of interest (e.g., the exact variable name to find the coordinates of)\n    :param require_unique: if True, raises an error if not exactly one match is found;\n        if False, returns None if no match is found, and returns the coordinates of the first match if multiple matches are found\n    :return: the coordinates of the match or None\n    \"\"\"\n    pattern = re.compile(regex, flags=re.MULTILINE | re.DOTALL)\n    matches = list(pattern.finditer(content))\n    if len(matches) == 0:\n        if require_unique:\n            raise ValueError(f\"No match found for regex: {regex}\")\n        return None\n    else:\n        if require_unique and len(matches) > 1:\n            raise ValueError(f\"Match must be unique; found {len(matches)} matches for regex: {regex}\")\n        match = matches[0]\n        if len(match.groups()) != 1:\n            raise ValueError(f\"Regex must contain exactly one group to capture the position, but found {len(match.groups())} groups.\")\n        index_in_content = match.start(1)\n        line, col = TextUtils.get_line_col_from_index(content, index_in_content)\n        return TextCoords(line, col)\n","sourceCodeStart":655,"sourceCodeEnd":677,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/util/text_utils.py#L655-L677","documentation":"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.","triggerScenarios":"Passing a regex with no parentheses, or with multiple capture groups like r'(def) (foo)', to find_text_coordinates.","commonSituations":"Reusing a search-only regex as a locator without adding a capturing group; adding non-capturing context as parentheses instead of (?:...).","solutions":["Wrap the token whose position you want in exactly one group: r'... (target) ...'","Convert auxiliary grouping parentheses to non-capturing (?:...)","Count groups with re.compile(pattern).groups before calling"],"exampleFix":"// before\nfind_text_coordinates(content, r'def (my_func)(args)')  # 2 groups\n// after\nfind_text_coordinates(content, r'def (my_func)(?:args)')  # exactly 1 group","handlingStrategy":"validation","validationCode":"g = re.compile(regex).groups\nif g != 1:\n    raise ValueError(f'locator regex needs exactly one capture group, has {g}')","typeGuard":"def has_single_group(regex: str) -> bool:\n    return re.compile(regex).groups == 1","tryCatchPattern":"try:\n    coords = find_text_coordinates(content, regex)\nexcept ValueError as e:\n    if 'exactly one group' in str(e):\n        coords = find_text_coordinates(content, f'({regex})')  # wrap single group\n    else:\n        raise","preventionTips":["Use non-capturing (?:...) for context groups","Check re.compile(p).groups == 1 before calling","Keep locator and search regexes separate"],"tags":["regex","capture-group","text-processing"],"backgroundTag":"regex-capture-group-mismatch","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}