babalae/better-genshin-impact · error · InvalidOperationException

武器名称 OCR 在 {MaxOcrAttempts} 次内未达到连续 {RequiredStableOcrCount}

Error message

武器名称 OCR 在 {MaxOcrAttempts} 次内未达到连续 {RequiredStableOcrCount} 次一致,最大连续次数={stableNames.MaxConsecutiveCount},末次原文={lastOcrText},末次匹配={lastMatchedName}

What it means

`ReadWeaponNameWithRetry` repeats OCR + `WeaponNameMatcher.Match` up to 10 times, requiring 3 consecutive identical *matched standard names* (and each match must be `IsReliable`). If it never stabilizes, it throws with the raw OCR text and the last matched candidate. This guards against recording an uncorrected or jittery weapon name.

Source

Thrown at BetterGenshinImpact/GameTask/CharacterDevelopment/CharacterDevelopmentTask.cs:994

                        attempt, MaxOcrAttempts, lastOcrText, match.Name, match.Distance, match.Similarity);
                }
                else
                {
                    var isStable = stableNames.Add(match.Name);
                    if (isStable)
                    {
                        return match.Name;
                    }
                }
            }

            if (attempt < MaxOcrAttempts)
            {
                await Delay(OcrRetryDelayMilliseconds, _ct);
            }
        }

        throw new InvalidOperationException(
            $"武器名称 OCR 在 {MaxOcrAttempts} 次内未达到连续 {RequiredStableOcrCount} 次一致," +
            $"最大连续次数={stableNames.MaxConsecutiveCount},末次原文={lastOcrText},末次匹配={lastMatchedName}");
    }

    private bool TryFindTalentPoints(ImageRegion capture, out List<Point> points)
    {
        var roi = GetTalentListRoi(capture);
        var regions = capture.FindMulti(RecognitionObject.Ocr(roi));
        try
        {
            var candidates = regions
                .Where(region => region.Text.Contains("Lv", StringComparison.OrdinalIgnoreCase))
                .OrderBy(region => region.Y + region.Height / 2)
                .ToList();
            var minDistance = Math.Max(1, (int)Math.Round(30 * _assetScale));
            points = [];
            foreach (var region in candidates)
            {

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Add the missing weapon to `item.csv` so `Match` can return a reliable, stable name.
  2. Verify the weapon-name ROI (`Rect1080(1465,132,346,42)`) matches the current layout.
  3. Loosen `WeaponNameMatcher` reliability thresholds only if false matches are acceptable.
  4. Increase `MaxOcrAttempts` for genuinely noisy captures.

Example fix

// before: weapon missing from item.csv → unreliable every attempt
// after: add the weapon row to Assets/Model/ItemV2/item.csv with item_class_id starting 'weapon:'
Defensive patterns

Strategy: retry

Validate before calling

// Ensure the weapon exists in the table before relying on Match:
if (!WeaponNames.Value.Contains(standardWeapon)) { /* add to item.csv */ }

Try / catch

try { await ReadWeaponNameWithRetry(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("武器名称 OCR"))
{ /* inspect lastOcrText/lastMatchedName; add weapon to item.csv or fix ROI */ }

Prevention

When it happens

Trigger: OCR text is too garbled for `Match` to be reliable every time (distance > 1 or similarity < 2/3); the weapon isn't in `item.csv` so the nearest match keeps changing; the weapon-name ROI captures the wrong line.

Common situations: Newly released weapon absent from `Assets/Model/ItemV2/item.csv`; resolution mismatch moving the weapon-name ROI onto another element; stylized weapon text confusing OCR.

Related errors


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