karatelabs/karate · error · RuntimeException

assert expression must return boolean:

Error message

assert expression must return boolean: 

What it means

Karate's 'assert' step requires its expression to evaluate to a boolean. If runtime.eval returns a non-Boolean (number, string, null, map), Karate throws this RuntimeException naming the offending expression, because the assertion result cannot be interpreted as pass/fail.

Solutions

  1. Wrap the expression in a comparison returning boolean: 'assert response.status != null'
  2. If checking existence, use a boolean operator (e.g. 'assert karate.sizeOf(list) > 0')
  3. Make helper functions return booleans, or compare their result explicitly
  4. Use karate.match / match steps instead of raw asserts for structural checks

Example fix

// before: expression is a value, not boolean
assert response
// after
assert response != null && response.id != null
Defensive patterns

Strategy: type-guard

Validate before calling

// evaluate the expression and check its type before using assert semantics
Object v = karate.eval(expression);
boolean isBool = v instanceof Boolean;

Type guard

static boolean isBooleanResult(Object v) { return v instanceof Boolean; }

Try / catch

try { executor.execute(assertStep); } catch (RuntimeException e) { if (e.getMessage().startsWith("assert expression must return boolean")) { throw new IllegalStateException("wrap expression in a comparison: " + e.getMessage()); } throw e; }

Prevention

When it happens

Trigger: An assert step whose expression yields a non-boolean value: 'assert response' (a map), 'assert someNumber', 'assert myString', or a function call returning a non-boolean.

Common situations: Assuming truthy/falsy JS semantics apply; asserting directly on response bodies or variables; calling a helper function that returns data instead of a comparison result.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

    /**
     * True for an expression like {@code ({ a: 1 })} or {@code ([1, 2])} — a JSON literal
     * the user forced through JS evaluation by wrapping it in round brackets.
     */
    private static boolean isParenWrappedJson(String expr) {
        if (expr.length() < 3 || expr.charAt(0) != '(' || expr.charAt(expr.length() - 1) != ')') {
            return false;
        }
        return StringUtils.looksLikeJson(expr.substring(1, expr.length() - 1).trim());
    }

    private void executeAssert(Step step) {
        Object result = runtime.eval(step.getText());
        if (result instanceof Boolean b) {
            if (!b) {
                throw new AssertionError(withCommentLabel(step, "assert failed: " + step.getText()));
            }
        } else {
            throw new RuntimeException("assert expression must return boolean: " + step.getText());
        }
    }

    private static final LogContext.LogWriter SCENARIO_LOG = LogContext.with(LogContext.SCENARIO_LOGGER);

    private void executePrint(Step step) {
        // Wrap in array to handle comma-separated expressions like: print 'foo', 'bar'
        // Without wrapping, JS comma operator would return only the last value
        Object value = runtime.eval("[" + step.getText() + "]");
        if (value instanceof List<?> list) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                if (i > 0) {
                    sb.append(' ');
                }
                sb.append(StepUtils.stringify(list.get(i)));
            }
            SCENARIO_LOG.info(sb.toString());

View on GitHub (pinned to a22eb90246)