{"record":{"id":"db634b8f51440fd4","repo":"babalae/better-genshin-impact","slug":"mainaffixtext","errorCode":null,"errorMessage":"未找到主词条对应的行：\n{mainAffixText}","messagePattern":"未找到主词条对应的行：\n(.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs","lineNumber":575,"sourceCode":"        (string Text, Rect Rect)[] levelAndMinorAffixResult = levelAndMinorAffixOcrResult.Regions.Where(r => r.Score > 0.5)\n            .Where(r => r.Rect.BoundingRect().Left < levelAndMinorAffixRoi.Width * 0.1) // 一定是贴着左边的，排除套装效果文字也存在类似+15%的情况\n            .OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X).Select(r => (r.Text, r.Rect.BoundingRect())).ToArray();\n        var levelAndMinorAffixLines = levelAndMinorAffixResult.Select(r => r.Text).ToArray();\n        allText = string.Join('\\n', new[]\n        {\n            nameOcrResult.Text,\n            typeOcrResult.Text,\n            mainAffixText\n        }.Concat(levelAndMinorAffixLines));\n\n        string percentStr = \"%\";\n\n        // 名称\n        string name = nameOcrResult.Text;\n\n        #region 主词条\n        var defaultMainAffix = this.artifactAffixStrDic.Select(kvp => kvp.Value).Distinct();\n        string mainAffixTypeLine = mainAffixLines.SingleOrDefault(l => defaultMainAffix.Contains(l)) ?? throw new Exception($\"未找到主词条对应的行：\\n{mainAffixText}\");\n        ArtifactAffixType mainAffixType = this.artifactAffixStrDic.First(kvp => kvp.Value == mainAffixTypeLine).Key;\n        string mainAffixValueLine = mainAffixLines.Select(l =>\n        {\n            string pattern = @\"^([\\d., ]*)(%?)$\";\n            pattern = pattern.Replace(\"%\", percentStr);   // 这样一行一行写只是为了IDE能保持正则字符串高亮\n            Match match = Regex.Match(l, pattern);\n            if (match.Success)\n            {\n                if (mainAffixType == ArtifactAffixType.ATK && !String.IsNullOrEmpty(match.Groups[2].Value))\n                {\n                    mainAffixType = ArtifactAffixType.ATKPercent;\n                }\n                if (mainAffixType == ArtifactAffixType.DEF && !String.IsNullOrEmpty(match.Groups[2].Value))\n                {\n                    mainAffixType = ArtifactAffixType.DEFPercent;\n                }\n                if (mainAffixType == ArtifactAffixType.HP && !String.IsNullOrEmpty(match.Groups[2].Value))\n                {","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs#L557-L593","documentation":"Thrown in GetArtifactStat when SingleOrDefault over mainAffixLines finds no line matching any localized main-affix name in artifactAffixStrDic (HP/ATK/DEF/CRIT/etc.). The OCR'd main-affix region did not contain a recognized affix-type label, so the artifact's main stat type cannot be determined.","triggerScenarios":"OCR returned empty/garbled text for the main-affix region (Score<=0.5 filtered everything); game language or culture does not match artifactAffixStrDic translations; the artifact card crop rect is wrong so the affix name is outside the ROI; threshold/morph preprocessing destroyed the text.","commonSituations":"Wrong in-game language vs configured cultureInfo; UI scale/resolution changed shifting the card layout; OCR model weakness on stylized fonts; a promotional/non-standard artifact with an unexpected affix.","solutions":["Verify the in-game language matches param.GameCultureInfo and the string localizer.","Inspect allText (the out param) to see what OCR actually captured for the main affix.","Confirm the card crop Rect percentages still match the current game UI layout.","Lower the OCR Score filter or re-run preprocessing if text is faint."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"bool HasKnownMainAffix(IEnumerable<string> lines, FrozenDictionary<ArtifactAffixType,string> dic)\n{\n    var names = dic.Values.Distinct().ToHashSet();\n    return lines.Any(l => names.Contains(l));\n}","typeGuard":null,"tryCatchPattern":"try { artifact = GetArtifactStat(card.SrcMat, ocr, out var text); }\ncatch (Exception e) { if (recognitionFailurePolicy == RecognitionFailurePolicy.Skip) continue; else throw; }","preventionTips":["Match in-game language to cultureInfo and the localizer.","Log allText (out param) on failure for diagnosis.","Verify card crop rects against the current UI layout.","Use RecognitionFailurePolicy.Skip to tolerate occasional OCR misses."],"tags":["game-task","ocr","artifact","recognition"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}