{"record":{"id":"3514cf8dda749425","repo":"karatelabs/karate","slug":"dynamic-expression-must-return-a-list-or-function-expression","errorCode":null,"errorMessage":"Dynamic expression must return a list or function: ${expression}, got: ${result != null ? result.getClass().getName() : \"null\"}","messagePattern":"Dynamic expression must return a list or function: (.+?), got: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/core/FeatureRuntime.java","lineNumber":750,"sourceCode":"                // Evaluate the dynamic expression in this runtime's engine. This runtime never\n                // goes through call(), so its client is released here or not at all — one leak\n                // per dynamic-outline feature, every run.\n                String expression = templateScenario.getDynamicExpression();\n                Object result;\n                try {\n                    result = sr.eval(expression);\n                } finally {\n                    sr.releaseHttpClient();\n                }\n\n                if (result instanceof List) {\n                    return (List<?>) result;\n                } else if (result instanceof JavaCallable) {\n                    // Generator function - call repeatedly until null/non-map\n                    return evaluateGeneratorFunction(result);\n                } else {\n                    // Expression didn't return a list or function - error\n                    throw new RuntimeException(\"Dynamic expression must return a list or function: \" + expression + \", got: \" + (result != null ? result.getClass().getName() : \"null\"));\n                }\n            } catch (Exception e) {\n                throw new RuntimeException(\"Failed to evaluate dynamic expression: \" + templateScenario.getDynamicExpression(), e);\n            }\n        }\n\n        /**\n         * Evaluates a generator function by calling it repeatedly with incrementing index\n         * until it returns null or a non-Map value.\n         */\n        @SuppressWarnings(\"unchecked\")\n        private List<Map<String, Object>> evaluateGeneratorFunction(Object function) {\n            List<Map<String, Object>> results = new ArrayList<>();\n            JavaCallable callable = (JavaCallable) function;\n            int index = 0;\n\n            while (true) {\n                Object rowValue;","sourceCodeStart":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/core/FeatureRuntime.java#L732-L768","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make the dynamic expression return a java.util.List of Maps (or a JS array) of scenario parameters","If using a function, ensure it is callable and returns Maps until done (null/non-Map terminates)","Log or print the expression result type in a scratch scenario to see what is actually returned","Wrap single-object results in a list, e.g. return [row] instead of return row"],"exampleFix":"// before\n* def data = { a: 1, b: 2 }\n// after\n* def data = [{ a: 1, b: 2 }]","handlingStrategy":"type-guard","validationCode":"// before using as dynamic expression\nvar result = <expression>;\nif (!Array.isArray(result)) { karate.abort('dynamic expression must return a list, got: ' + typeof result); }","typeGuard":"function isRowList(v) { return v instanceof Array && v.every(function (x) { return x instanceof Map || typeof x === 'object'; }); }","tryCatchPattern":"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; }","preventionTips":["Always return arrays (even single-element) from dynamic expressions","Return null (not {}) from generator functions to terminate iteration","Print the expression result once during development to confirm its type"],"tags":["dynamic-scenarios","examples-table","javascript","type-mismatch"],"backgroundTag":"unexpected-response-shape","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}