{"record":{"id":"8845a1f58e52d982","repo":"babalae/better-genshin-impact","slug":"current","errorCode":null,"errorMessage":"键谱出现不匹配的括号：{current}","messagePattern":"键谱出现不匹配的括号：(.+?)","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs","lineNumber":508,"sourceCode":"                return nodes;\n            }\n\n            KeyboardNode node;\n            if (current is '(' or '[' or '{')\n            {\n                index++;\n                var expectedClosing = current switch\n                {\n                    '(' => ')',\n                    '[' => ']',\n                    '{' => '}',\n                    _ => throw new InvalidOperationException()\n                };\n                node = new KeyboardNode(current, null, ParseKeyboardNodes(text, ref index, expectedClosing));\n            }\n            else if (current is ')' or ']' or '}')\n            {\n                throw new FormatException($\"键谱出现不匹配的括号：{current}\");\n            }\n            else if (current == '-')\n            {\n                if (nodes.Count == 0)\n                {\n                    throw new FormatException(\"键谱延音符号前没有音符\");\n                }\n\n                nodes[^1].Multiplier++;\n                index++;\n                continue;\n            }\n            else\n            {\n                node = new KeyboardNode('\\0', current, []);\n                index++;\n            }\n","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs#L490-L526","documentation":"A FormatException from the recursive ParseKeyboardNodes helper thrown when it encounters a closing bracket ')', ']', or '}' that does not match the currently-open group. Each opening bracket sets an expected closing character; a different closer (or a stray closer at the top level) is treated as unmatched.","triggerScenarios":"A keyboard-score string after preprocessing contains a closing bracket without a matching opener, e.g. 'Q])', 'A})', or starts with ')'. Mismatched pairs like '(Q]' (open '(' but close ']') also trip it.","commonSituations":"Manually authored keyboard scores with mismatched brackets; preprocessing transforms (the /(...) and /[...]/ replacements around line 452-456) producing an unexpected closer; truncated score text.","solutions":["Balance all brackets in the keyboard score so every '(' has a ')', '[' a ']', and '{' a '}'.","Re-create the score and let the editor bracket-match as you type.","Run the score through ParseAsync so the error surfaces as InvalidScore.Error with the bad character.","Add a quick bracket-balance lint before invoking parse to point at the offending position."],"exampleFix":"// before\n\"notes\":\"(Q]\"\n// after\n\"notes\":\"(Q)\"","handlingStrategy":"validation","validationCode":"static bool BracketsBalanced(string s)\n{\n    var pairs = new Dictionary<char,char>{{')','('},{']','['},{'}','{'}};\n    var st = new Stack<char>();\n    foreach (var c in s)\n    {\n        if (\"([{\".Contains(c)) st.Push(c);\n        else if (pairs.ContainsKey(c) && (st.Count == 0 || st.Pop() != pairs[c])) return false;\n    }\n    return st.Count == 0;\n}","typeGuard":"null","tryCatchPattern":"var score = await parser.ParseAsync(path, root, ct);\nif (!string.IsNullOrEmpty(score.Error)) { Log.Warning(score.Error); continue; }","preventionTips":["Lint keyboard scores for bracket balance before parsing.","Use a bracket-matching editor when authoring.","Route through ParseAsync for InvalidScore reporting."],"tags":["music-score","keyboard-score","input-validation","parsing"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}