karatelabs/karate · error · JsErrorException
Generator is already running
Error message
Generator is already running
What it means
generator.return(value) was called while the generator's activation is currently executing (running). Per the generator protocol a generator cannot be resumed from within its own frame, so the engine throws a TypeError instead of corrupting the activation stack. This is surfaced by JsGenerator.returnValue when isDone/isStarted checks pass but the activation is marked running.
Solutions
- Move the gen.return(...) call outside the generator body, after the driving loop completes.
- Use a flag/queue so the inner code signals completion instead of calling return() directly.
- If the body should stop, throw or return from inside the body rather than calling gen.return() on itself.
- Review with a call-flow check whether any closure invoked by the generator retains the generator reference.
Example fix
// before (inside generator body invoked callback) onDone: () => gen.return(result) // after: signal via flag, call return() outside onDone: () => finished = true; // ...later, outside the generator frame: if (finished) gen.return(result);
Defensive patterns
Strategy: try-catch
Validate before calling
// no caller-side pre-check exists for running state; avoid re-entrant gen.return() by design
Try / catch
try { gen.return(v); } catch (e) { if (String(e).indexOf('already running') !== -1) { pendingReturn = v; } else { throw e; } } Prevention
- Never call gen.return()/gen.throw() from callbacks invoked by the generator body
- Use flags or queues to signal completion across the generator boundary
- Keep generator driving code in one place (a single loop/driver)
When it happens
Trigger: Calling gen.return(x) from inside the generator's own body (e.g. via a callback or helper the generator invokes), or re-entering the generator recursively through a function that closes over it.
Common situations: Generator body delegates to a callback that calls gen.return() to 'finish early'; recursive iteration helpers; async-like code that tries to finalize a generator from a listener it started.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Generator is already running
- iterator result is not an object
- The iterator does not provide a 'throw' method
- iterator.throw is not a function
- Method Generator.prototype called on incompatible receiver
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/addd4a4be5021c71.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/JsGenerator.java:64
static JsObject result(Object value, boolean done) {
JsObject r = new JsObject();
r.putMember("value", value);
r.putMember("done", done);
return r;
}
Object next(CoreContext ctx, Object value) {
if (activation.isDone()) {
return result(Terms.UNDEFINED, true);
}
return drive(ctx, ResumeKind.NEXT, value);
}
Object returnValue(CoreContext ctx, Object value) {
if (activation.isDone() || !activation.isStarted()) {
if (activation.isRunning()) {
throw JsErrorException.typeError("Generator is already running");
}
activation.retire(); // NOT_STARTED: no body code runs
return result(value, true);
}
return drive(ctx, ResumeKind.RETURN, value);
}
Object throwValue(CoreContext ctx, Object value) {
if (activation.isDone() || !activation.isStarted()) {
if (activation.isRunning()) {
throw JsErrorException.typeError("Generator is already running");
}
activation.retire(); // NOT_STARTED: no body code runs
ctx.stopAndThrow(value);
return Terms.UNDEFINED;
}
return drive(ctx, ResumeKind.THROW, value);
}View on GitHub (pinned to a22eb90246)