karatelabs/karate · error · JsErrorException

Generator is already running

Error message

Generator is already running

What it means

The generator machinery (GeneratorActivation.step) throws this TypeError when a generator whose state is already RUNNING is asked to take another step. This corresponds to the ECMAScript rule that you cannot call next()/return()/throw() on a generator from inside its own execution (e.g. via its iterator indirectly). The state check is an AtomicReference compare so concurrent host callers also funnel here (line 151 is the direct state==RUNNING check).

Solutions

  1. Do not consume the generator from within its own execution — collect results first, then iterate
  2. If re-entrancy is needed, create a second independent generator instance
  3. Buffer yielded values into an array/queue and iterate the buffer inside the generator body
  4. In host code, synchronize or confine each generator to a single thread

Example fix

// before
function* g() { for (const v of g()) yield v; } // self-consumption
// after
function* g(src) { for (const v of src) yield v * 2; }
const it = g([1,2,3]);
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure you never iterate a generator inside its own body:
// pass the source collection in as a parameter instead of self-referencing

Type guard

function canStep(gen) { return gen && !gen.__running; }

Try / catch

try { var r = gen.next(); } catch (e) { if (String(e).includes('Generator is already running')) { /* restructure: don't re-enter */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling gen.next() (directly or through spread/for-of) from within the generator's own body or from a callback invoked while the generator is executing; re-entrant resume through shared state; two host threads stepping the same generator activation.

Common situations: Generator yields into a callback that itself consumes the same iterator; recursive iteration over a generator that feeds itself; multithreaded host code sharing a generator instance across threads.

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


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/GeneratorActivation.java:151

    }

    /** {@code return(v)} / {@code throw(e)} on a NOT_STARTED or DONE generator
     *  runs no body code — just retire it. */
    void retire() {
        state.set(State.DONE);
    }

    /**
     * One driver step: deposit the resume input, hand the coroutine the lock,
     * park until it yields / returns / throws, and return the outcome. Caller
     * (JsGenerator) has already done brand/state shortcuts; this method owns
     * the RUNNING transition.
     */
    StepOutcome step(ResumeKind kind, Object value) {
        anyGenerators = true;
        State s = state.get();
        if (s == State.RUNNING) {
            throw JsErrorException.typeError("Generator is already running");
        }
        if (s == State.DONE) {
            // raced with a concurrent completion — treat as done
            return new StepOutcome(OutcomeKind.RETURNED, Terms.UNDEFINED);
        }
        if (!state.compareAndSet(s, State.RUNNING)) {
            // a concurrent host caller won the step; spec answer is the same
            // "already running" TypeError
            throw JsErrorException.typeError("Generator is already running");
        }
        AtomicReference<StepOutcome> cell = new AtomicReference<>();
        stepCell = cell;
        resumeKind = kind;
        resumeValue = value;
        driver = Thread.currentThread();
        // register-only-if-open: a RUNNING step is scope-owned, cancellable
        // work; a closed scope must not accept it
        AsyncScope scope = engine.currentScope();

View on GitHub (pinned to a22eb90246)