{"record":{"id":"fb6116844a3110b6","repo":"pypa/pip","slug":"invalid-glob-r-mismatching-set-marker-or","errorCode":null,"errorMessage":"invalid glob %r: mismatching set marker '{' or '}'","messagePattern":"invalid glob %r: mismatching set marker '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distlib/util.py","lineNumber":1441,"sourceCode":"\n\n#\n# Glob functionality\n#\n\nRICH_GLOB = re.compile(r'\\{([^}]*)\\}')\n_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\\\,{]\\*\\*|\\*\\*[^/\\\\,}]')\n_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\\}|\\{[^}]*$')\n\n\ndef iglob(path_glob):\n    \"\"\"Extended globbing function that supports ** and {opt1,opt2,opt3}.\"\"\"\n    if _CHECK_RECURSIVE_GLOB.search(path_glob):\n        msg = \"\"\"invalid glob %r: recursive glob \"**\" must be used alone\"\"\"\n        raise ValueError(msg % path_glob)\n    if _CHECK_MISMATCH_SET.search(path_glob):\n        msg = \"\"\"invalid glob %r: mismatching set marker '{' or '}'\"\"\"\n        raise ValueError(msg % path_glob)\n    return _iglob(path_glob)\n\n\ndef _iglob(path_glob):\n    rich_path_glob = RICH_GLOB.split(path_glob, 1)\n    if len(rich_path_glob) > 1:\n        assert len(rich_path_glob) == 3, rich_path_glob\n        prefix, set, suffix = rich_path_glob\n        for item in set.split(','):\n            for path in _iglob(''.join((prefix, item, suffix))):\n                yield path\n    else:\n        if '**' not in path_glob:\n            for item in std_iglob(path_glob):\n                yield item\n        else:\n            prefix, radical = path_glob.split('**', 1)\n            if prefix == '':","sourceCodeStart":1423,"sourceCodeEnd":1459,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/distlib/util.py#L1423-L1459","documentation":"Raised as ValueError by distlib.util.iglob when the pattern has mismatched set markers '{' or '}'. The regex _CHECK_MISMATCH_SET matches a '}' with no preceding '{' in the leading portion, or a '{' with no following '}' to the end — i.e. unbalanced brace-set syntax. The extended globber uses '{a,b,c}' for alternation sets; an orphan brace cannot be parsed.","triggerScenarios":"iglob('src/{python,ruby'), iglob('src/python}'), or any pattern where '{' and '}' counts are unbalanced or appear out of order. Valid 'src/{a,b}/c' is accepted.","commonSituations":"Dynamically building alternation sets and dropping a closing brace; config-supplied glob patterns with typos; concatenating partial patterns that break brace nesting.","solutions":["Ensure every '{' has a matching '}' and they are correctly ordered.","Validate brace balance before calling: pattern.count('{') == pattern.count('}').","Build alternation sets from a list and format with '{%s}' % ','.join(options)."],"exampleFix":"# before\niglob('tests/{unit,integration')\n# after\niglob('tests/{unit,integration}')","handlingStrategy":"validation","validationCode":"def valid_glob(pattern):\n    return pattern.count('{') == pattern.count('}') and '}' not in pattern.split('{')[0]","typeGuard":"def has_balanced_braces(s: str) -> bool:\n    return isinstance(s, str) and s.count('{') == s.count('}')","tryCatchPattern":"try:\n    list(iglob(pattern))\nexcept ValueError as e:\n    if 'mismatching set marker' in str(e):\n        pattern += '}' if pattern.count('{') > pattern.count('}') else ''","preventionTips":["Ensure every '{' has a matching '}'.","Build alternation sets from lists, not string concat."],"tags":["glob","distlib","filesystem","pattern-matching","brace-set"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}