{"record":{"id":"0f90a980c94d926a","repo":"jdx/mise","slug":"unopened-brace-alternate-in-glob-pattern-pattern","errorCode":null,"errorMessage":"unopened brace alternate in glob pattern {pattern:?}","messagePattern":"unopened brace alternate in glob pattern (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_source_checker.rs","lineNumber":101,"sourceCode":"                continue;\n            }\n            match ch {\n                '[' if class_depth == 0 => class_depth = 1,\n                ']' if class_depth == 1 => class_depth = 0,\n                '{' if class_depth == 0 => {\n                    if group_depth == 0 {\n                        group_start = Some(idx);\n                        branch_start = idx + ch.len_utf8();\n                    }\n                    group_depth += 1;\n                }\n                ',' if class_depth == 0 && group_depth == 1 => {\n                    branches.push((branch_start, idx));\n                    branch_start = idx + ch.len_utf8();\n                }\n                '}' if class_depth == 0 => {\n                    if group_depth == 0 {\n                        bail!(\"unopened brace alternate in glob pattern {pattern:?}\");\n                    }\n                    group_depth -= 1;\n                    if group_depth == 0 {\n                        let start = group_start.unwrap();\n                        if !branches.is_empty() {\n                            branches.push((branch_start, idx));\n                            return Ok(Some(BraceGroup {\n                                start,\n                                end: idx,\n                                branches,\n                            }));\n                        }\n\n                        // A balanced group without a top-level comma is a\n                        // literal brace pair. It may still contain a nested\n                        // alternate, so look inside it before continuing with\n                        // the remainder of the pattern.\n                        if let Some(mut nested) = find_group(&pattern[start + 1..idx])? {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_source_checker.rs#L83-L119","documentation":"When expanding brace alternates (`{a,b}`) in task glob patterns, find_group scans for `{`/`}`. A `}` encountered while group_depth == 0 means there is no matching opening brace, so the pattern is malformed and the function bails. This protects glob expansion from silently treating an unbalanced pattern as a literal.","triggerScenarios":"Passing a task source/output glob containing `}` without a preceding `{` to expand_glob_braces, e.g. via task file patterns like `dist/*.js}` or an escaped brace typo.","commonSituations":"Hand-edited task globs in mise.toml where an opening `{` was deleted or mis-escaped; copy-paste from docs that dropped the `{`; glob classes `[}]` mistaken for brace syntax.","solutions":["Add the missing opening `{` or remove the stray `}` in the glob pattern","Escape a literal brace as `\\{`/`\\}` if it should match literally","Validate the pattern by running the task after editing mise.toml"],"exampleFix":"// before (mise.toml)\nsources = ['src/*.ts}']\n// after\nsources = ['src/{a,b}/*.ts']","handlingStrategy":"validation","validationCode":"// js: quick brace-balance check for task globs before writing config\nfunction bracesBalanced(p) {\n  let d = 0;\n  for (const ch of p.replace(/\\\\./g, '')) {\n    if (ch === '{') d++;\n    if (ch === '}') { if (--d < 0) return false; }\n  }\n  return d === 0;\n}\nbracesBalanced('src/{a,b}/*.ts'); // true","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint mise.toml globs for balanced braces","Escape literal braces as \\{ and \\}","Prefer simple wildcards over brace alternates where possible"],"tags":["glob","task","brace-expansion"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}