{"record":{"id":"d922f7e361fcadfb","repo":"babalae/better-genshin-impact","slug":"fieldname-ocr-text","errorCode":null,"errorMessage":"无法从 {fieldName} OCR 结果中解析等级：{text}","messagePattern":"无法从 (.+?) OCR 结果中解析等级：(.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/CharacterDevelopment/CharacterDevelopmentTask.cs","lineNumber":1162,"sourceCode":"                break;\n            case SkillTalentType:\n                result.SkillLevel = level;\n                result.SkillHasBonus = hasBonus;\n                break;\n            case BurstTalentType:\n                result.BurstLevel = level;\n                result.BurstHasBonus = hasBonus;\n                break;\n            default:\n                throw new InvalidOperationException($\"未知天赋类型：{type}\");\n        }\n    }\n\n    internal static (int Level, int Limit) ParseLevelPair(string text, string fieldName)\n    {\n        if (!TryParseLevelPair(text, out var level, out var limit))\n        {\n            throw new InvalidOperationException($\"无法从 {fieldName} OCR 结果中解析等级：{text}\");\n        }\n\n        return (level, limit);\n    }\n\n    internal static bool TryParseLevelPair(string text, out int level, out int limit)\n    {\n        level = 0;\n        limit = 0;\n        var matches = NumberRegex.Matches(text);\n        return matches.Count >= 2\n               && int.TryParse(matches[0].Value, out level)\n               && int.TryParse(matches[1].Value, out limit)\n               && level > 0\n               && limit > 0;\n    }\n\n    private static string OcrText(ImageRegion capture, Rect roi)","sourceCodeStart":1144,"sourceCodeEnd":1180,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/CharacterDevelopment/CharacterDevelopmentTask.cs#L1144-L1180","documentation":"`ParseLevelPair` is the throwing variant of `TryParseLevelPair`: it requires at least two digit-runs in the OCR text and both to be > 0. If the level text doesn't contain two positive integers (e.g. '/90', 'Lv', or garbage), parsing is impossible and it throws with the offending text.","triggerScenarios":"OCR returned partial text (only one number, or none); the ROI captured the wrong area; the level display uses a format the `\\d+` regex splits differently; text like 'Lv.90' with a single visible number.","commonSituations":"Misaligned level ROI after a layout/resolution change; OCR dropping digits due to stylized fonts; the level cap not yet rendered.","solutions":["Prefer the non-throwing `TryParseLevelPair` at call sites that already handle instability (the retry loops do).","Re-align the level ROI to the current in-game layout.","Improve OCR preprocessing so two numbers are reliably captured.","If only calling `ParseLevelPair` directly, validate the text contains two numbers first."],"exampleFix":"// before\nvar (lvl, lim) = ParseLevelPair(text, \"角色等级\"); // throws on 'Lv/90'\n\n// after\nif (TryParseLevelPair(text, out var lvl, out var lim))\n    use(lvl, lim);\nelse\n    logAndRetry(text);","handlingStrategy":"type-guard","validationCode":"if (!CharacterDevelopmentTask.TryParseLevelPair(text, out var level, out var limit))\n    return; // or retry; avoid the throwing ParseLevelPair at unstable call sites","typeGuard":"static bool HasTwoPositiveNumbers(string text)\n{\n    var m = Regex.Matches(text, @\"\\d+\");\n    return m.Count >= 2 && m.Cast<Match>().All(x => int.Parse(x.Value) > 0);\n}","tryCatchPattern":null,"preventionTips":["Prefer TryParseLevelPair over ParseLevelPair at call sites with unstable OCR.","Re-align the level ROI when the layout changes.","Validate two positive numbers exist before calling ParseLevelPair directly."],"tags":["parsing","ocr","level","regex","character-development"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}