babalae/better-genshin-impact · error · InvalidDataException

武器名称表中没有可用的武器。

Error message

武器名称表中没有可用的武器。

What it means

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.

Source

Thrown at BetterGenshinImpact/GameTask/CharacterDevelopment/WeaponNameMatcher.cs:56

    public static WeaponNameMatch Match(string ocrText)
    {
        return MatchClosest(ocrText, WeaponNames.Value);
    }

    /// <summary>
    /// 在给定名称表中选择编辑距离最小的名称,并判断该候选是否足够可信。
    /// </summary>
    internal static WeaponNameMatch MatchClosest(string ocrText, IReadOnlyList<string> weaponNames)
    {
        var normalizedText = ocrText.Trim();
        if (string.IsNullOrWhiteSpace(normalizedText))
        {
            throw new InvalidOperationException("武器名称 OCR 结果为空。");
        }

        if (weaponNames.Count == 0)
        {
            throw new InvalidDataException("武器名称表中没有可用的武器。");
        }

        var candidates = weaponNames
            .Select(name =>
            {
                var distance = LevenshteinDistance(normalizedText, name);
                var maximumLength = Math.Max(normalizedText.Length, name.Length);
                var similarity = 1d - (double)distance / maximumLength;
                return new WeaponNameMatch(name, distance, similarity, false);
            })
            .OrderBy(match => match.Distance)
            .ThenBy(match => match.Name, StringComparer.Ordinal)
            .Take(2)
            .ToArray();

        var best = candidates[0];
        var distanceMargin = candidates.Length == 1
            ? int.MaxValue

View on GitHub (pinned to a7cb36712d)

Solutions

  1. If calling MatchClosest directly, guard with a non-empty check before invoking it.
  2. Verify the bundled Assets/Model/ItemV2/item.csv exists and contains rows whose item_class_id starts with 'weapon:'.
  3. Rebuild/reinstall the application from a known-good source if the asset file is corrupted.

Example fix

// before
var match = WeaponNameMatcher.MatchClosest(ocrText, myWeaponNames);

// after
if (myWeaponNames.Count == 0) { /* handle: no weapon data loaded */ return; }
var match = WeaponNameMatcher.MatchClosest(ocrText, myWeaponNames);
Defensive patterns

Strategy: validation

Validate before calling

if (weaponNames == null || weaponNames.Count == 0)
{
    // handle missing weapon data — do not call MatchClosest
    return;
}

Type guard

static bool HasWeaponNames(IReadOnlyList<string> names) => names != null && names.Count > 0;

Try / catch

try { var match = WeaponNameMatcher.MatchClosest(text, names); }
catch (InvalidDataException) { /* weapon name table empty — log and skip */ }

Prevention

When it happens

Trigger: 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>.

Common situations: 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.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/75a018791fd14230. Report an issue: GitHub.