karatelabs/karate · error · RuntimeException

Dynamic expression must return a list or function

Error message

Dynamic expression must return a list or function: ${expression}, got: ${result != null ? result.getClass().getName() : "null"}

What it means

When a Scenario uses a dynamic expression (e.g. Examples with a 'karate.setup...' style expression), Karate evaluates it and expects the result to be a List of Maps or a JavaCallable generator function. If the evaluated result is any other type (or the expression itself is malformed), this RuntimeException is thrown listing the expression and the actual Java class of the result.

Solutions

  1. Make the dynamic expression return a java.util.List of Maps (or a JS array) of scenario parameters
  2. If using a function, ensure it is callable and returns Maps until done (null/non-Map terminates)
  3. Log or print the expression result type in a scratch scenario to see what is actually returned
  4. Wrap single-object results in a list, e.g. return [row] instead of return row

Example fix

// before
* def data = { a: 1, b: 2 }
// after
* def data = [{ a: 1, b: 2 }]
Defensive patterns

Strategy: type-guard

Validate before calling

// before using as dynamic expression
var result = <expression>;
if (!Array.isArray(result)) { karate.abort('dynamic expression must return a list, got: ' + typeof result); }

Type guard

function isRowList(v) { return v instanceof Array && v.every(function (x) { return x instanceof Map || typeof x === 'object'; }); }

Try / catch

try { evaluate(expression) } catch (RuntimeException e) { if (e.getMessage().startsWith('Dynamic expression must return a list')) { log.error('Return a List<Map> or generator function from: ' + expression); } throw e; }

Prevention

When it happens

Trigger: A dynamic scenario expression returns a single Map instead of a list, a String, a number, or null-typed object that is neither List nor JavaCallable — e.g. 'Examples: <expression>' where the expression evaluates to one row object rather than a collection of rows.

Common situations: A helper JS function accidentally returns one object instead of an array; a Java helper method returns a Map; the expression is wrapped in quotes so it evaluates as a string; forgetting that the expression must produce multiple scenarios.

Related errors


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

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/FeatureRuntime.java:750

                // Evaluate the dynamic expression in this runtime's engine. This runtime never
                // goes through call(), so its client is released here or not at all — one leak
                // per dynamic-outline feature, every run.
                String expression = templateScenario.getDynamicExpression();
                Object result;
                try {
                    result = sr.eval(expression);
                } finally {
                    sr.releaseHttpClient();
                }

                if (result instanceof List) {
                    return (List<?>) result;
                } else if (result instanceof JavaCallable) {
                    // Generator function - call repeatedly until null/non-map
                    return evaluateGeneratorFunction(result);
                } else {
                    // Expression didn't return a list or function - error
                    throw new RuntimeException("Dynamic expression must return a list or function: " + expression + ", got: " + (result != null ? result.getClass().getName() : "null"));
                }
            } catch (Exception e) {
                throw new RuntimeException("Failed to evaluate dynamic expression: " + templateScenario.getDynamicExpression(), e);
            }
        }

        /**
         * Evaluates a generator function by calling it repeatedly with incrementing index
         * until it returns null or a non-Map value.
         */
        @SuppressWarnings("unchecked")
        private List<Map<String, Object>> evaluateGeneratorFunction(Object function) {
            List<Map<String, Object>> results = new ArrayList<>();
            JavaCallable callable = (JavaCallable) function;
            int index = 0;

            while (true) {
                Object rowValue;

View on GitHub (pinned to a22eb90246)