{"record":{"id":"be6c1133a2cefc67","repo":"oraios/serena","slug":"invalid-glob-brace-expression-in-pattern-r-unma","errorCode":null,"errorMessage":"Invalid glob brace expression in {pattern!r}: unmatched brace","messagePattern":"Invalid glob brace expression in (.+?): unmatched brace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/serena/util/text_utils.py","lineNumber":237,"sourceCode":"        Handles multiple brace sets as well.\n        \"\"\"\n        patterns = [pattern]\n        while any(\"{\" in p or \"}\" in p for p in patterns):\n            new_patterns = []\n            for p in patterns:\n                match = re.search(r\"\\{([^{}]*)\\}\", p)\n                if match:\n                    options_expr = match.group(1)\n                    if not options_expr:\n                        raise ValueError(f\"Invalid glob brace expression in {pattern!r}: empty braces are not allowed\")\n\n                    prefix = p[: match.start()]\n                    suffix = p[match.end() :]\n                    options = options_expr.split(\",\")\n                    for option in options:\n                        new_patterns.append(f\"{prefix}{option}{suffix}\")\n                else:\n                    raise ValueError(f\"Invalid glob brace expression in {pattern!r}: unmatched brace\")\n            patterns = new_patterns\n        return patterns\n\n    @staticmethod\n    def _translate_glob_to_regex(pattern: str) -> str:\n        res = []\n        i = 0\n        n = len(pattern)\n        while i < n:\n            c = pattern[i]\n            i += 1\n            if c == \"*\":\n                if i < n and pattern[i] == \"*\":  # **\n                    i += 1\n                    if i < n and pattern[i] == \"/\":  # **/\n                        i += 1\n                        if res and res[-1] == \"/\":  # /**/\n                            res = res[:-1]","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/util/text_utils.py#L219-L255","documentation":"_expand_braces loops expanding innermost brace groups while a {...} matches; if braces remain unbalanced (an opening brace with no matching close), the regex finds nothing and it raises ValueError 'unmatched brace'. This prevents silently treating malformed globs as literal patterns.","triggerScenarios":"Passing patterns like 'src/*{.py' or 'a{b,c' where brace counts don't balance — the expansion loop terminates without consuming all braces and raises.","commonSituations":"Hand-written glob patterns with typos; string concatenation building patterns that cut off mid-brace; partially substituted templates.","solutions":["Balance the braces in the pattern, e.g. 'src/*.{py,pyi}'","Remove braces if no alternation is intended","Pre-validate the pattern with a quick brace-balance check before calling","Catch ValueError and surface a pattern-syntax error to the user"],"exampleFix":"// before\npattern = 'src/*{.py'\n// after\npattern = 'src/*.{py}'  # or simply 'src/*.py'","handlingStrategy":"validation","validationCode":"def braces_balanced(pattern: str) -> bool:\n    return pattern.count(\"{\") == pattern.count(\"}\")\nif not braces_balanced(glob):\n    raise ValueError(f\"unbalanced braces in {glob!r}\")","typeGuard":null,"tryCatchPattern":"try:\n    expanded = GlobPattern(pat)\nexcept ValueError as e:\n    if \"unmatched brace\" in str(e):\n        log.error(\"Fix pattern syntax: %s\", pat)\n    raise","preventionTips":["Balance-check braces before using brace-expansion globs","Avoid building patterns via string concatenation that may truncate mid-brace","Test glob expansion on representative inputs in unit tests"],"tags":["python","glob","valueerror","pattern-validation"],"backgroundTag":"invalid-glob-pattern","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}