{"record":{"id":"9ae9b9383c3020a2","repo":"karatelabs/karate","slug":"async-function-called-outside-an-engine-evaluation","errorCode":null,"errorMessage":"async function called outside an engine evaluation","messagePattern":"async function called outside an engine evaluation","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/AsyncSupport.java","lineNumber":188,"sourceCode":"        } catch (RuntimeException e) {\n            if (e instanceof FlowControlSignal || isHostCancellation(e)) {\n                throw e;\n            }\n            if (e instanceof AwaitRejection ar) {\n                return new Completion(null, ar.reason, true);\n            }\n            return new Completion(null, reasonOf(e), true);\n        }\n    }\n\n    //=== activations ==================================================================================================\n\n    /** Entry point from {@link JsFunctionNode#bindArgsAndExecute}: an async\n     *  invocation returns its promise, never its body's value. */\n    static Object callAsync(JsFunctionNode function, CoreContext functionContext, Object[] args) {\n        Engine engine = functionContext.getEngine();\n        if (engine == null) {\n            throw JsErrorException.typeError(\"async function called outside an engine evaluation\");\n        }\n        AsyncScope scope = engine.currentScope();\n        if (scope != null) {\n            return AsyncActivation.spawn(engine, scope, function, functionContext, args);\n        }\n        return hostAsyncCall(engine, function, functionContext, args);\n    }\n\n    /**\n     * A Java caller invoking an async JS function directly, outside any eval:\n     * open a scope for the call, spawn, drain to quiescence, and hand back the\n     * promise (never an auto-awaited value — the host decides).\n     */\n    private static Object hostAsyncCall(Engine engine, JsFunctionNode function,\n                                        CoreContext functionContext, Object[] args) {\n        engine.checkPoisoned();\n        boolean outermost = engine.enterEvalScope();\n        AsyncScope scope = engine.currentScope();","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/AsyncSupport.java#L170-L206","documentation":"Karate's JS engine throws this when an async JS function is invoked but no Engine is associated with the current evaluation context. Async invocations need an engine to resolve the current AsyncScope (or fall back to a host async call); without one the result promise could never be managed, so the library fails fast with a TypeError instead of silently misbehaving.","triggerScenarios":"Calling a JS `async function` via AsyncSupport.callAsync when functionContext.getEngine() returns null — e.g. invoking the function outside of Engine.evalRaw/eval, or holding a JsFunctionNode from one engine and executing it after that evaluation has ended.","commonSituations":"Caching a JS function and calling it later from a plain Java thread; invoking a Karate JS function from a custom step or framework hook that bypasses the engine; calling async functions during engine teardown.","solutions":["Run the call inside an Engine evaluation (e.g. Engine.evalRaw / within the context that produced the function).","Ensure the JsFunctionNode's CoreContext was created by and still references a live Engine.","Do not call bound JS functions from arbitrary worker threads — schedule work through the engine's async machinery instead."],"exampleFix":"// before: function called from plain Java thread\nObject result = AsyncSupport.callAsync(fn, bareContext, args);\n// after: evaluate inside an engine context\nObject result = engine.evalRaw(\"myAsyncFn()\", args);","handlingStrategy":"type-guard","validationCode":"if (context.getEngine() == null) throw new IllegalStateException(\"call async functions inside an Engine evaluation\");","typeGuard":"function hasEngine(ctx) { return ctx != null && ctx.getEngine() != null; }","tryCatchPattern":"try { return AsyncSupport.callAsync(fn, ctx, args); } catch (JsErrorException e) { if (e.getMessage().contains(\"outside an engine evaluation\")) { /* re-run inside engine */ } else throw e; }","preventionTips":["Only execute JS functions through Engine evaluation entry points.","Never retain and reuse JsFunctionNode/CoreContext after an evaluation completes.","Route background work through the engine's async scheduling instead of direct calls."],"tags":["javascript","async","engine-lifecycle"],"backgroundTag":"invalid-state-transition","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"}