babalae/better-genshin-impact · error · InvalidOperationException

缺少右括号

Error message

缺少右括号

What it means

Thrown by ConditionEvaluator.ParsePrimary when a parenthesized sub-expression (opened with '(') is parsed but the closing ')' is not found at the expected position. This is a syntax error in the condition expression: an unbalanced '('. When called via Evaluate(), this is caught internally and Evaluate returns false.

Solutions

  1. Count and balance all '(' and ')' in the condition expression.
  2. The expression engine does not support implicit grouping — every '(' must have a matching ')'.
  3. Simplify complex nested expressions and rebuild them incrementally, testing each part.
  4. Use the Evaluate method in a dry-run with logging to identify the exact failure point.

Example fix

// before (unclosed paren)
"condition": "(q-ready() && low-hp"

// after
"condition": "(q-ready() && low-hp())"
Defensive patterns

Strategy: try-catch

Validate before calling

// Quick balance check for parentheses in a condition expression
static bool AreParensBalanced(string expr)
{
    int depth = 0;
    foreach (var c in expr)
    {
        if (c == '(') depth++;
        if (c == ')') depth--;
        if (depth < 0) return false;
    }
    return depth == 0;
}

if (!AreParensBalanced(conditionExpr))
    Log.LogWarning("条件表达式括号不平衡: {Expr}", conditionExpr);

Try / catch

// Evaluate catches this internally (line 147-151) and returns false.
// No additional try-catch needed when using Evaluate.
// For safety, log the expression when Evaluate returns unexpected false:
var result = evaluator.Evaluate(expr, index, charName, actionName);
if (!result && /* expected true context */)
    Logger.LogWarning("条件求值返回false,可能表达式有语法错误: {Expr}", expr);

Prevention

When it happens

Trigger: A condition expression with an unclosed parenthesis, e.g. `(q-ready() && low-hp` or `since(1 > 5`. The parser enters ParsePrimary expecting ')' after the inner expression, but finds a different token (End, or another operator/identifier).

Common situations: User writes a JSON strategy condition with a missing ')', or has unbalanced parentheses from manual editing. For example: `"condition": "(q-ready() && low-hp"` — the outer '(' is never closed.

Related errors


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

Appendix: source

Thrown at BetterGenshinImpact/GameTask/AutoFight/Script/ConditionEvaluator.cs:341

        {
            var op = tokens[pos].Value; pos++;
            return new UnaryOpNode(op, ParseUnaryExpr(tokens, ref pos));
        }
        if (tokens[pos].Type == TokenType.Minus)
        {
            pos++;
            return new UnaryOpNode("-u", ParseUnaryExpr(tokens, ref pos));
        }
        return ParsePrimary(tokens, ref pos);
    }

    private static AstNode ParsePrimary(List<Token> tokens, ref int pos)
    {
        if (tokens[pos].Type == TokenType.LParen)
        {
            pos++;
            var node = ParseOrExpr(tokens, ref pos);
            if (tokens[pos].Type != TokenType.RParen) throw new InvalidOperationException("缺少右括号");
            pos++;
            return node;
        }

        if (tokens[pos].Type == TokenType.Identifier)
        {
            var name = tokens[pos].Value; pos++;
            if (tokens[pos].Type == TokenType.LParen)
            {
                pos++;
                var args = new List<AstNode>();
                if (tokens[pos].Type != TokenType.RParen)
                {
                    args.Add(ParseOrExpr(tokens, ref pos));
                    while (tokens[pos].Type == TokenType.Comma)
                    {
                        pos++;
                        args.Add(ParseOrExpr(tokens, ref pos));

View on GitHub (pinned to a7cb36712d)