babalae/better-genshin-impact · error · FormatException

键谱缺少右括号:{closing.Value}

Error message

键谱缺少右括号:{closing.Value}

What it means

A FormatException from ParseKeyboardNodes thrown at end-of-text when a closing parameter was expected (closing.HasValue) but never matched. Each recursive call expects its specific closer; reaching the end of the string without it means an opening bracket was never closed.

Source

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

            }
            else
            {
                node = new KeyboardNode('\0', current, []);
                index++;
            }

            while (index < text.Length && text[index] == '-')
            {
                node.Multiplier++;
                index++;
            }

            nodes.Add(node);
        }

        if (closing.HasValue)
        {
            throw new FormatException($"键谱缺少右括号:{closing.Value}");
        }

        return nodes;
    }

    private static void UnfoldKeyboardNode(
        KeyboardNode node,
        double startBeat,
        double durationBeat,
        ICollection<KeyboardNote> output)
    {
        if (node.Value.HasValue)
        {
            var key = char.ToUpperInvariant(node.Value.Value);
            if (key != '0' && SupportedKeys.Contains(key))
            {
                output.Add(new KeyboardNote(key, startBeat, 0, durationBeat));
            }

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Add the missing closing bracket indicated by {closing.Value} (e.g. append ')' for an unclosed '(').
  2. Use a bracket-matching editor to verify the structure.
  3. Route through ParseAsync to surface InvalidScore.Error.
  4. Lint the score for bracket balance before parsing.

Example fix

// before
"notes":"(QW-"
// after
"notes":"(QW-)"
Defensive patterns

Strategy: validation

Validate before calling

static bool AllBracketsClosed(string s)
{
    var st = new Stack<char>();
    foreach (var c in s)
    {
        if (c is '(' or '[' or '{') st.Push(c);
        else if (c is ')' or ']' or '}') { if (st.Count == 0) return false; st.Pop(); }
    }
    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 with an unclosed bracket, e.g. '(QW', '[AB', or '{CD'. The message echoes the expected closing character.

Common situations: Truncated score; manual editing that deleted a closing bracket; a group nested incorrectly so the closer landed at the wrong scope.

Related errors


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