babalae/better-genshin-impact · error · KeyNotFoundException

未找到区域别名

Error message

未找到区域别名 {alias}

What it means

A region (ROI) expression may start with '@' to reference a named alias from the JSON regions map. ResolveRegionExpression dereferences it and throws KeyNotFoundException when the alias is undefined, so the rectangle cannot be resolved.

Solutions

  1. Declare the region alias in the regions map, e.g. "regions": { "topBar": "rect(0,0,cw,40)" }.
  2. Inline the rect expression directly in roi if the alias is not needed.
  3. Check alias spelling and case against the regions map keys.

Example fix

// before
// "roi": "@topBar"  with no regions.topBar -> throws

// after
// "regions": { "topBar": "rect(0, 0, cw, 40)" },
// "objects": { "x": { "roi": "@topBar" } }
Defensive patterns

Strategy: validation

Validate before calling

foreach (var (name, cfg) in config.Objects)
{
    if (cfg.Roi is { } r && r.TrimStart().StartsWith('@'))
    {
        var alias = r.Trim()[1..];
        if (!config.Regions.ContainsKey(alias))
            throw new InvalidOperationException($"Object '{name}' references undefined region alias '@{alias}'.");
    }
}

Try / catch

try { return RecognitionObjectJsonLoader.Load(config, objectName, context); }
catch (KeyNotFoundException ex) when (ex.Message.Contains("未找到区域别名"))
{ Logger.LogError(ex, "Undefined region alias referenced."); throw; }

Prevention

When it happens

Trigger: An object has "roi": "@topBar" but the Recognition.json defines no regions.topBar entry.

Common situations: Region alias removed/renamed but still referenced; typo in the alias; alias defined in a shared file not loaded.

Related errors


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

Appendix: source

Thrown at BetterGenshinImpact/Core/Recognition/RecognitionObjectJsonLoader.cs:339

        {
            var resolved = ResolveRegionExpression(expression);
            var value = EvaluateValue(resolved);
            return value switch
            {
                Rect rect => rect,
                _ => throw new InvalidOperationException($"表达式 {expression} 未返回 Rect")
            };
        }

        private string ResolveRegionExpression(string expression)
        {
            var resolved = expression.Trim();
            while (resolved.StartsWith('@'))
            {
                var alias = resolved[1..];
                if (!_config.Regions.TryGetValue(alias, out resolved))
                {
                    throw new KeyNotFoundException($"未找到区域别名 {alias}");
                }
            }

            return resolved;
        }

        private object EvaluateValue(string expression, string? skipVarName = null)
        {
            var normalizedExpression = NormalizeExpression(expression);
            var ncalcExpression = new Expression(normalizedExpression);

            foreach (var (name, value) in BuildParameters(skipVarName))
            {
                ncalcExpression.Parameters[name] = value;
            }

            ncalcExpression.Functions["rect"] = args => BuildRect(args.Evaluate(0), args.Evaluate(1), args.Evaluate(2), args.Evaluate(3));
            ncalcExpression.Functions["cutLeft"] = args => _captureRect.CutLeft(ToDouble(args.Evaluate(0)));

View on GitHub (pinned to a7cb36712d)