{"record":{"id":"ae6e3c5fde462a88","repo":"karatelabs/karate","slug":"maximum-call-stack-size-exceeded","errorCode":null,"errorMessage":"Maximum call stack size exceeded","messagePattern":"Maximum call stack size exceeded","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsFunctionNode.java","lineNumber":302,"sourceCode":"    Object bindArgsAndExecute(CoreContext functionContext, CoreContext parentContext, Object[] args) {\n        functionContext.privateEnv = privateEnv;\n        if (generator) {\n            // A generator call runs no body code — it returns the generator\n            // object; the body executes on the generator's vthread one driver\n            // step at a time. Parameter binding is deferred to the first\n            // next(), the same documented deviation async has.\n            Engine engine = functionContext.getEngine();\n            return new JsGenerator(engine, this, functionContext, args);\n        }\n        if (async) {\n            // Argument binding is part of the activation's startup, so it runs on\n            // the activation thread under the startup-outcome protocol — not here.\n            return AsyncSupport.callAsync(this, functionContext, args);\n        }\n        try {\n            return executeBody(functionContext, parentContext, args);\n        } catch (StackOverflowError e) {\n            throw JsErrorException.rangeError(\"Maximum call stack size exceeded\");\n        }\n    }\n\n    /** The synchronous body run. For an async function this is what the\n     *  activation thread executes; the caller has already been handed a promise. */\n    Object executeBody(CoreContext functionContext, CoreContext parentContext, Object[] args) {\n        // Attach the slot frame here — the single choke point every call path\n        // shares before params bind and defaults evaluate. For an async function\n        // this runs on the activation thread, so the frame exists before the\n        // body's first statement there too; the frame lives on the context and\n        // follows it across await suspensions.\n        SlotTable table = slotTable;\n        if (table == null && SlotTable.ENABLED && ++callCount == 2) {\n            table = SlotTable.forNodeForced(node, argNodes, body);\n            slotTable = table;\n        }\n        if (table != null) {\n            functionContext.frame = table.newFrame();","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsFunctionNode.java#L284-L320","documentation":"During synchronous function execution, a Java StackOverflowError escaping executeBody is translated into this JS RangeError so scripts get a normal, catchable JS error instead of crashing the host. It fires when JS recursion (direct or mutual) exceeds the thread stack.","triggerScenarios":"Unbounded recursion like function f(){ return f(); } f(), deep recursion on large nested data without a base case, mutually recursive functions with no termination, extremely deep data structures walked recursively.","commonSituations":"Recursive JSON/tree walkers over deeply nested API payloads, recursive template or formula evaluators, accidental infinite recursion after a refactor removed a base case.","solutions":["Add or fix the recursion base case so it terminates.","Convert recursion to iteration (explicit stack/loop) for deep data.","Catch the RangeError at the boundary and fail gracefully with a diagnostic.","Increase the host thread stack size (-Xss) only as a last resort."],"exampleFix":"// before\nfunction sum(arr, i) { return arr[i] + sum(arr, i + 1); } // no base case\n// after\nfunction sum(arr, i) { return i >= arr.length ? 0 : arr[i] + sum(arr, i + 1); }","handlingStrategy":"try-catch","validationCode":"let depth = 0; function checkDepth() { if (++depth > 5000) throw new Error('recursion too deep'); }","typeGuard":null,"tryCatchPattern":"try { return fn(); } catch (e) { if (e instanceof RangeError && /Maximum call stack/.test(e.message)) return { error: 'stack-overflow' }; throw e; }","preventionTips":["Guarantee every recursive function has a reachable base case.","Cap recursion depth explicitly with a depth counter.","Use iterative traversals (explicit stack) for unbounded-depth data.","Bound input nesting depth when accepting external JSON payloads."],"tags":["javascript","recursion","stack-overflow","range-error"],"backgroundTag":"value-out-of-range","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"}