babalae/better-genshin-impact · warning · FormatException

曲谱缺少 notes 字段

Error message

曲谱缺少 notes 字段

What it means

Thrown by MusicScoreParser.ParseJsonAsync when the parsed JSON object does not contain a 'notes' property (json["notes"] is null). The 'notes' field is mandatory for all supported score types (yuanqin, midi, keyboard). Note: at the outer ParseAsync level, this FormatException is caught and converted into an invalid PerformanceScore with an Error field, so it does not crash the application.

Source

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

        }
        catch (Exception e)
        {
            return CreateInvalidScore(path, rootFolder, e.Message);
        }
    }

    private static async Task<PerformanceScore> ParseJsonAsync(
        string path,
        string rootFolder,
        CancellationToken cancellationToken)
    {
        var text = await File.ReadAllTextAsync(path, cancellationToken);
        var json = JObject.Parse(text);
        var type = GetString(json, "type", "yuanqin").Trim().ToLowerInvariant();
        var bpm = GetDouble(json, "bpm", 120);
        var ticks = Math.Max(1, (int)Math.Round(GetDouble(json, "ticks", 480)));
        var timeSignature = GetString(json, "time_signature", "4/4");
        var notes = json["notes"] ?? throw new FormatException("曲谱缺少 notes 字段");

        var format = type switch
        {
            "yuanqin" => MusicScoreFormat.YuanQin,
            "midi" => MusicScoreFormat.MidiJson,
            "keyboard" => MusicScoreFormat.Keyboard,
            _ => throw new FormatException($"不支持的 AutoYuanQin 曲谱类型:{type}")
        };

        var timeline = format switch
        {
            MusicScoreFormat.YuanQin => ParseYuanQinTimeline(notes, bpm, timeSignature),
            MusicScoreFormat.MidiJson => ParseMidiJsonTimeline(GetNotesString(notes, format), bpm, ticks),
            MusicScoreFormat.Keyboard => ParseKeyboardTimeline(GetNotesString(notes, format), bpm),
            _ => PerformanceTimeline.Empty
        };

        return new PerformanceScore

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Open the score JSON file and add a 'notes' field (string for yuanqin/keyboard, array for yuanqin object form, or string for midi format).
  2. Validate the JSON schema before parsing — check for 'notes' presence with a JSON schema validator.
  3. Refer to existing valid score files in the project for the correct schema.
  4. Check the PerformanceScore.Error property after ParseAsync to surface the message to the user gracefully.

Example fix

// before
var notes = json["notes"] ?? throw new FormatException("曲谱缺少 notes 字段");

// after — include the file path for diagnosis
var notes = json["notes"] ?? throw new FormatException(
    $"曲谱缺少 notes 字段,文件:{Path.GetFileName(path)}");
Defensive patterns

Strategy: validation

Validate before calling

var json = JObject.Parse(text);
if (json["notes"] == null)
{
    logger.LogError("曲谱缺少 notes 字段");
    return; // or return invalid score
}

Type guard

static bool HasNotesField(JObject json) => json["notes"] != null;

Try / catch

var score = await parser.ParseAsync(path, rootFolder, ct);
if (!string.IsNullOrEmpty(score.Error)) { logger.LogWarning("曲谱解析失败:{Error}", score.Error); }

Prevention

When it happens

Trigger: Loading a .json score file that has valid JSON but lacks the top-level 'notes' key. The JObject.Parse succeeds, but json["notes"] returns null.

Common situations: A user-edited or auto-generated score file omits the notes field; the file uses a different schema; a template/stub file was saved without content.

Related errors


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