karatelabs/karate · warning

retry condition evaluation failed

Error message

retry condition evaluation failed: {}

What it means

Karate evaluates a @retry-until condition by evaluating an expression via the runtime script engine. If the expression fails to evaluate (syntax error, undefined variable, non-boolean result), Karate logs this warning, treats the condition as not met, and continues retrying instead of failing the step. The message interpolates only e.getMessage(), which can be unhelpfully terse (e.g. '{}').

Solutions

  1. Fix the retry expression so it evaluates to a JS boolean and only references variables that exist at evaluation time
  2. Check the adjacent log line for e.getMessage(); reproduce the expression in a * print statement to see the actual engine error
  3. Guard the expression against missing data, e.g. retry-until responseStatus == 200 && response.foo != undefined && response.foo == 'x'
  4. If the condition should stop retries on error, implement a custom loop with * configure retry = ... or manual JS with karate.call for clearer diagnostics

Example fix

// before
* configure retry = { count: 5, interval: 2000 }
* get /items
* retry until response.data.status == 'DONE'
// after (guard against undefined so evaluation never throws)
* configure retry = { count: 5, interval: 2000 }
* get /items
* retry until responseStatus == 200 && response.data && response.data.status == 'DONE'
Defensive patterns

Strategy: validation

Validate before calling

// before relying on retry, smoke-test the expression in the same feature
* def __retryOk =
  """
  (function(){ try { return !! (response.data && response.data.status == 'DONE'); }
                catch(e){ karate.log('retry expr error: ' + e); return false; } })()
  """

Type guard

function safeRetryExpr(fn){ try { return !!fn(); } catch (e) { karate.log('retry expr failed: ' + e); return false; } }

Prevention

When it happens

Trigger: A scenario uses retry-until / configure retry with an expression that throws when evaluated: referencing an undefined variable, a syntax error, calling a method that throws, or the expression returning null/non-Boolean so TRUE.equals fails logically (though that path does not warn).

Common situations: Typo in a response-scoped variable before the first request runs; JSON path or JS syntax errors in the retry expression; a helper function not yet defined when retry starts; copy-pasted expressions from another dialect (e.g. Groovy/Python).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/0e1115c5c5773b00. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/StepExecutor.java:2382

                // Request skipped by listener
                response = HttpResponse.skipped(request);
            }

            // Fire HTTP_EXIT event (always, even for skipped)
            if (suite != null) {
                suite.fireEvent(HttpRunEvent.exit(response.getRequest(), response, runtime));
            }

            // Set response variables so the condition can access them
            setResponseVariables(response);

            // Evaluate retry condition
            boolean conditionMet;
            try {
                Object result = runtime.eval(retryUntil);
                conditionMet = Boolean.TRUE.equals(result);
            } catch (Exception e) {
                logger.warn("retry condition evaluation failed: {}", e.getMessage());
                conditionMet = false;
            }

            if (conditionMet) {
                if (retryCount > 0) {
                    logger.debug("retry condition satisfied after {} attempts", retryCount + 1);
                }
                return response;
            } else {
                logger.debug("retry condition not satisfied: {}", retryUntil);
            }

            // Restore request state for next retry (http().invoke() resets it)
            http().restoreFrom(httpCopy);

            retryCount++;
        }
    }

View on GitHub (pinned to a22eb90246)