babalae/better-genshin-impact · error · FormatException

yuanqin 音符对象缺少 note 字段

Error message

yuanqin 音符对象缺少 note 字段

What it means

A FormatException raised inside ParseYuanQinTokens while iterating the "notes" array of a yuanqin-format AutoYuanQin JSON score. Each element must be an object carrying a non-empty "note" field (the key letters). GetString returns an empty fallback when the field is absent or null, and IsNullOrWhiteSpace then trips this throw. It is a contract violation in the score file, not a transient runtime fault.

Source

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

        }

        return tokens;
    }

    private static List<YuanQinToken> ParseYuanQinTokens(JArray notes)
    {
        var tokens = new List<YuanQinToken>(notes.Count);
        foreach (var item in notes)
        {
            if (item is not JObject note)
            {
                throw new FormatException("yuanqin 音符数组中包含非对象元素");
            }

            var keys = GetString(note, "note", string.Empty);
            if (string.IsNullOrWhiteSpace(keys))
            {
                throw new FormatException("yuanqin 音符对象缺少 note 字段");
            }

            var special = GetString(note, "spl", "none");
            if (special is "^" or "&")
            {
                tokens.Add(new YuanQinToken(keys, 0, special));
                continue;
            }

            if (!double.TryParse(
                    note["type"]?.ToString(),
                    NumberStyles.Float,
                    CultureInfo.InvariantCulture,
                    out var denominator))
            {
                throw new FormatException($"无法解析音符 {keys} 的时值");
            }

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Open the offending .json file and confirm every object in the notes array has a non-empty "note" field (e.g. {"note":"Q","type":4}).
  2. If you generated the file with a converter, regenerate it and verify the converter emits the "note" key, not a synonym.
  3. At the public API surface (ParseAsync) this throw is already swallowed and converted into a PerformanceScore with Error set, so surface score.Error to the user instead of letting the raw exception propagate.
  4. Add a JSON-schema pre-check on yuanqin files before parsing so malformed notes are reported with the offending index.

Example fix

// before: note object missing the field
{"type":"yuanqin","bpm":120,"notes":[{"type":4}]}
// after: every note carries "note"
{"type":"yuanqin","bpm":120,"notes":[{"note":"Q","type":4}]}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate a yuanqin notes array before parsing
static bool HasValidYuanQinNotes(JToken notes)
{
    if (notes is not JArray arr) return false;
    foreach (var item in arr)
    {
        if (item is not JObject o) return false;
        var n = o["note"]?.ToString();
        if (string.IsNullOrWhiteSpace(n)) return false;
    }
    return true;
}

Type guard

static bool IsYuanQinNoteObject(JToken t) => t is JObject o && !string.IsNullOrWhiteSpace(o["note"]?.ToString());

Try / catch

// ParseAsync already converts this to an InvalidScore; check the result
var score = await parser.ParseAsync(path, root, ct);
if (!string.IsNullOrEmpty(score.Error))
{
    Log.Warning("跳过无效曲谱 {Path}: {Error}", path, score.Error);
    continue;
}

Prevention

When it happens

Trigger: Parsing a JSON score whose top-level "type" resolves to "yuanqin" where the "notes" JArray contains an element that is a JObject but has no "note" key, or whose "note" value is an empty/whitespace string.

Common situations: Hand-edited or converter-generated yuanqin files where a note object was dropped to {}; scores exported by a tool that renamed the key (e.g. "key"/"k" instead of "note"); a truncated/copy-paste file missing fields.

Related errors


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