{"record":{"id":"239bb3b00d22961e","repo":"babalae/better-genshin-impact","slug":"type","errorCode":null,"errorMessage":"未知天赋类型：{type}","messagePattern":"未知天赋类型：(.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/CharacterDevelopment/CharacterDevelopmentTask.cs","lineNumber":1154,"sourceCode":"    internal static void ApplyTalentResult(CharacterDevelopmentResult result, string type, int level, bool hasBonus)\n    {\n        ArgumentNullException.ThrowIfNull(result);\n        switch (type)\n        {\n            case AttackTalentType:\n                result.AttackLevel = level;\n                result.AttackHasBonus = hasBonus;\n                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);","sourceCodeStart":1136,"sourceCodeEnd":1172,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/CharacterDevelopment/CharacterDevelopmentTask.cs#L1136-L1172","documentation":"`ApplyTalentResult` switches on the normalized talent type and throws for any value outside {普通攻击, 元素战技, 元素爆发}. Because `ReadTalentDetailWithRetry` only returns types produced by `NormalizeTalentType` (which returns exactly those three or empty), reaching the default branch indicates an internal invariant break.","triggerScenarios":"`ApplyTalentResult` called directly with an unnormalized/empty type string; `NormalizeTalentType` extended with a new type not added to the switch; a stale type value passed from a test.","commonSituations":"Unit tests calling `ApplyTalentResult` with arbitrary strings; future talent-type additions that forget to extend the switch.","solutions":["Only pass `NormalizeTalentType` output into `ApplyTalentResult`.","When adding a talent type constant, update `NormalizeTalentType`, the switch, and the completeness check (error 330) together.","In tests, derive the type from `NormalizeTalentType` rather than hardcoding."],"exampleFix":"// before\nApplyTalentResult(result, \"普攻\", 8, false); // not a normalized type → throws\n\n// after\nvar type = NormalizeTalentType(rawTitle); // \"普通攻击\"\nApplyTalentResult(result, type, level, hasBonus);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static bool IsKnownTalentType(string t) =>\n    t == \"普通攻击\" || t == \"元素战技\" || t == \"元素爆发\";","tryCatchPattern":null,"preventionTips":["Only pass NormalizeTalentType output into ApplyTalentResult.","When adding a talent type, update NormalizeTalentType, the switch, and the completeness check together.","In tests, derive types from NormalizeTalentType rather than literals."],"tags":["switch-enum","invariant","talent","character-development"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}