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.

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)

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.