{"record":{"id":"676f55c343384ff7","repo":"babalae/better-genshin-impact","slug":"keys-676f55","errorCode":null,"errorMessage":"无法解析音符 {keys} 的时值","messagePattern":"无法解析音符 (.+?) 的时值","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs","lineNumber":367,"sourceCode":"            if (string.IsNullOrWhiteSpace(keys))\n            {\n                throw new FormatException(\"yuanqin 音符对象缺少 note 字段\");\n            }\n\n            var special = GetString(note, \"spl\", \"none\");\n            if (special is \"^\" or \"&\")\n            {\n                tokens.Add(new YuanQinToken(keys, 0, special));\n                continue;\n            }\n\n            if (!double.TryParse(\n                    note[\"type\"]?.ToString(),\n                    NumberStyles.Float,\n                    CultureInfo.InvariantCulture,\n                    out var denominator))\n            {\n                throw new FormatException($\"无法解析音符 {keys} 的时值\");\n            }\n\n            tokens.Add(new YuanQinToken(keys, denominator, special));\n        }\n\n        return tokens;\n    }\n\n    private static PerformanceTimeline ParseMidiJsonTimeline(string sheet, double initialBpm, int ticks)\n    {\n        var events = new List<PerformanceEvent>();\n        var cursor = TimeSpan.Zero;\n        var bpm = initialBpm;\n        string previousStatus = string.Empty;\n        string previousKeys = string.Empty;\n        var previousWasEvent = false;\n        var rawParts = sheet.Split(\n            '|',","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs#L349-L385","documentation":"A FormatException from ParseYuanQinTokens thrown when the \"type\" field of a yuanqin note object cannot be parsed as a floating-point number. The \"type\" value is the beat denominator (e.g. 4 = quarter note) used to compute duration; a missing, null, or non-numeric value leaves the note without a valid duration. The parse uses NumberStyles.Float with the invariant culture, so locale-specific decimals like commas are rejected.","triggerScenarios":"A yuanqin note object whose \"type\" field is absent (note[\"type\"]?.ToString() yields null/empty), set to a non-numeric string (\"quarter\"), or uses a comma decimal separator under a non-invariant convention.","commonSituations":"Scores edited in a spreadsheet that reformatted the type column to text; converter bug that omitted type on non-special notes; note objects where \"type\" was mistakenly stored as the note name.","solutions":["Ensure each non-special yuanqin note has a numeric \"type\" (e.g. 1, 2, 4, 8, 0.5) written with a dot decimal separator.","Regenerate the score from its source tool and re-export.","Because ParseAsync catches this, the file becomes an InvalidScore; inspect score.Error to find which file failed.","Add a per-note validator that reports the index of the offending note before parsing."],"exampleFix":"// before\n{\"note\":\"Q\",\"type\":\"quarter\"}\n// after\n{\"note\":\"Q\",\"type\":4}","handlingStrategy":"try-catch","validationCode":"static bool HasValidYuanQinTypes(JToken notes)\n{\n    if (notes is not JArray arr) return false;\n    foreach (var item in arr)\n    {\n        if (item is not JObject o) continue;\n        var spl = o[\"spl\"]?.ToString();\n        if (spl is \"^\" or \"&\") continue;\n        var t = o[\"type\"]?.ToString();\n        if (!double.TryParse(t, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) || d <= 0)\n            return false;\n    }\n    return true;\n}","typeGuard":"static bool IsValidNoteType(JObject o) => double.TryParse(o[\"type\"]?.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out _);","tryCatchPattern":"var score = await parser.ParseAsync(path, root, ct);\nif (!string.IsNullOrEmpty(score.Error)) { Log.Warning(score.Error); continue; }","preventionTips":["Use dot decimal separators in exported type values.","Validate before import to report the bad note index.","Rely on ParseAsync's InvalidScore conversion."],"tags":["music-score","json-parsing","input-validation","yuanqin"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}