babalae/better-genshin-impact · error · InvalidOperationException

表达式 {expression} 未返回 Rect

Error message

表达式 {expression} 未返回 Rect

What it means

EvaluateRect runs the ROI expression through NCalc and expects a Rect result (produced by the rect()/cutXxx() helper functions). If the expression evaluates to a scalar number or any non-Rect value, it throws InvalidOperationException, because a region of interest must be a rectangle.

Source

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

            {
                var alias = resolved[1..];
                if (!_config.Templates.TryGetValue(alias, out resolved))
                {
                    throw new KeyNotFoundException($"未找到模板别名 {alias}");
                }
            }

            return resolved;
        }

        private Rect EvaluateRect(string expression)
        {
            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;
        }

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Wrap the expression in rect(x, y, width, height), e.g. "rect(0, 0, cw * 0.5, ch)".
  2. Use a cutXxx(...) helper or a region alias that resolves to a Rect-producing expression.
  3. Confirm the expression returns a Rect by testing it before deployment.

Example fix

// before (Recognition.json)
// "roi": "cw * 0.5"  -> evaluates to a number -> throws

// after
// "roi": "rect(0, 0, cw * 0.5, ch)"
Defensive patterns

Strategy: validation

Validate before calling

// Evaluate the ROI expression in a dry-run and assert the result type
using NCalc.Expression e = new(expr);
// supply cw/ch/cx/cy/s parameters as in the loader
var result = e.Evaluate();
if (result is not OpenCvSharp.Rect)
    throw new InvalidOperationException($"ROI expression '{expr}' must return a Rect (use rect(...)/cutXxx(...)).");

Type guard

static bool IsRectExpression(string expression, IDictionary<string, object> parameters)
{
    try
    {
        var e = new NCalc.Expression(expression);
        foreach (var (k, v) in parameters) e.Parameters[k] = v;
        return e.Evaluate() is OpenCvSharp.Rect;
    }
    catch { return false; }
}

Try / catch

try { return RecognitionObjectJsonLoader.Load(config, objectName, context); }
catch (InvalidOperationException ex) when (ex.Message.Contains("未返回 Rect"))
{ Logger.LogError(ex, "ROI expression did not evaluate to a Rect."); throw; }

Prevention

When it happens

Trigger: A RecognitionObject has "roi": "cw * 0.5" (a bare number) instead of "roi": "rect(0, 0, cw, ch)" or a cutXxx(...) call.

Common situations: ROI confused with a numeric threshold; forgot to wrap dimensions in rect(); referenced a region alias that resolves to a number.

Related errors


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