{"record":{"id":"75a018791fd14230","repo":"babalae/better-genshin-impact","slug":"error-75a018","errorCode":null,"errorMessage":"武器名称表中没有可用的武器。","messagePattern":"武器名称表中没有可用的武器。","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/CharacterDevelopment/WeaponNameMatcher.cs","lineNumber":56,"sourceCode":"    public static WeaponNameMatch Match(string ocrText)\n    {\n        return MatchClosest(ocrText, WeaponNames.Value);\n    }\n\n    /// <summary>\n    /// 在给定名称表中选择编辑距离最小的名称，并判断该候选是否足够可信。\n    /// </summary>\n    internal static WeaponNameMatch MatchClosest(string ocrText, IReadOnlyList<string> weaponNames)\n    {\n        var normalizedText = ocrText.Trim();\n        if (string.IsNullOrWhiteSpace(normalizedText))\n        {\n            throw new InvalidOperationException(\"武器名称 OCR 结果为空。\");\n        }\n\n        if (weaponNames.Count == 0)\n        {\n            throw new InvalidDataException(\"武器名称表中没有可用的武器。\");\n        }\n\n        var candidates = weaponNames\n            .Select(name =>\n            {\n                var distance = LevenshteinDistance(normalizedText, name);\n                var maximumLength = Math.Max(normalizedText.Length, name.Length);\n                var similarity = 1d - (double)distance / maximumLength;\n                return new WeaponNameMatch(name, distance, similarity, false);\n            })\n            .OrderBy(match => match.Distance)\n            .ThenBy(match => match.Name, StringComparer.Ordinal)\n            .Take(2)\n            .ToArray();\n\n        var best = candidates[0];\n        var distanceMargin = candidates.Length == 1\n            ? int.MaxValue","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/CharacterDevelopment/WeaponNameMatcher.cs#L38-L74","documentation":"Thrown by WeaponNameMatcher.MatchClosest when the weaponNames collection passed to it is empty (Count == 0). The internal lazy-loaded name table is populated from item.csv; this error means either the CSV failed to yield any weapon names or a caller passed an empty list. The guard exists so the subsequent LINQ OrderBy/Take does not produce an empty candidates array that would cause an IndexOutOfRangeException.","triggerScenarios":"Calling WeaponNameMatcher.Match(ocrText) when the lazy WeaponNames.Value resolved to zero entries — i.e., ExtractWeaponNames returned an empty set that somehow bypassed its own empty-set guard, or calling MatchClosest directly with an empty IReadOnlyList<string>.","commonSituations":"The item.csv shipped with the build is missing or truncated so no weapon: rows exist (normally caught earlier by [342], but a corrupt build could let an empty list through). A custom caller unit-testing MatchClosest passes an empty weapon name list.","solutions":["If calling MatchClosest directly, guard with a non-empty check before invoking it.","Verify the bundled Assets/Model/ItemV2/item.csv exists and contains rows whose item_class_id starts with 'weapon:'.","Rebuild/reinstall the application from a known-good source if the asset file is corrupted."],"exampleFix":"// before\nvar match = WeaponNameMatcher.MatchClosest(ocrText, myWeaponNames);\n\n// after\nif (myWeaponNames.Count == 0) { /* handle: no weapon data loaded */ return; }\nvar match = WeaponNameMatcher.MatchClosest(ocrText, myWeaponNames);","handlingStrategy":"validation","validationCode":"if (weaponNames == null || weaponNames.Count == 0)\n{\n    // handle missing weapon data — do not call MatchClosest\n    return;\n}","typeGuard":"static bool HasWeaponNames(IReadOnlyList<string> names) => names != null && names.Count > 0;","tryCatchPattern":"try { var match = WeaponNameMatcher.MatchClosest(text, names); }\ncatch (InvalidDataException) { /* weapon name table empty — log and skip */ }","preventionTips":["Verify item.csv is present and contains weapon rows before running weapon OCR correction.","If calling MatchClosest directly, always pre-check the list is non-empty."],"tags":["ocr","weapon-matcher","csv-data","data-validation"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}