babalae/better-genshin-impact · error · FormatException

键谱出现不匹配的括号:{current}

Error message

键谱出现不匹配的括号:{current}

What it means

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.

Source

Thrown at BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs:508

                return nodes;
            }

            KeyboardNode node;
            if (current is '(' or '[' or '{')
            {
                index++;
                var expectedClosing = current switch
                {
                    '(' => ')',
                    '[' => ']',
                    '{' => '}',
                    _ => throw new InvalidOperationException()
                };
                node = new KeyboardNode(current, null, ParseKeyboardNodes(text, ref index, expectedClosing));
            }
            else if (current is ')' or ']' or '}')
            {
                throw new FormatException($"键谱出现不匹配的括号:{current}");
            }
            else if (current == '-')
            {
                if (nodes.Count == 0)
                {
                    throw new FormatException("键谱延音符号前没有音符");
                }

                nodes[^1].Multiplier++;
                index++;
                continue;
            }
            else
            {
                node = new KeyboardNode('\0', current, []);
                index++;
            }

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Balance all brackets in the keyboard score so every '(' has a ')', '[' a ']', and '{' a '}'.
  2. Re-create the score and let the editor bracket-match as you type.
  3. Run the score through ParseAsync so the error surfaces as InvalidScore.Error with the bad character.
  4. Add a quick bracket-balance lint before invoking parse to point at the offending position.

Example fix

// before
"notes":"(Q]"
// after
"notes":"(Q)"
Defensive patterns

Strategy: validation

Validate before calling

static bool BracketsBalanced(string s)
{
    var pairs = new Dictionary<char,char>{{')','('},{']','['},{'}','{'}};
    var st = new Stack<char>();
    foreach (var c in s)
    {
        if ("([{".Contains(c)) st.Push(c);
        else if (pairs.ContainsKey(c) && (st.Count == 0 || st.Pop() != pairs[c])) return false;
    }
    return st.Count == 0;
}

Type guard

null

Try / catch

var score = await parser.ParseAsync(path, root, ct);
if (!string.IsNullOrEmpty(score.Error)) { Log.Warning(score.Error); continue; }

Prevention

When it happens

Trigger: 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.

Common situations: Manually authored keyboard scores with mismatched brackets; preprocessing transforms (the /(...) and /[...]/ replacements around line 452-456) producing an unexpected closer; truncated score text.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/8845a1f58e52d982. Report an issue: GitHub.