oracle/graal · error · IllegalContinuationStateException

This continuation has failed and must be discarded.

Error message

This continuation has failed and must be discarded.

What it means

IllegalContinuationStateException raised by resume() when the continuation previously failed (state FAILED), e.g. its entry point threw an escaping exception. A failed continuation is tainted and must be discarded; resuming is not possible.

Source

Thrown at espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java:379

                clearExclusiveOwner();
            }
        } else if (updateState(State.SUSPENDED, State.RUNNING)) {
            setExclusiveOwner();
            try {
                // We are ready to resume, make sure the VM has the most up-to-date frames
                ensureDematerialized();
                assert stackFrameHead == null;
                return resume0();
            } finally {
                clearExclusiveOwner();
            }
        } else {
            // illegal state for resume: neither suspended nor new
            switch (state) {
                case RUNNING -> throw new IllegalContinuationStateException("You can't resume an already executing continuation.");
                case COMPLETED ->
                    throw new IllegalContinuationStateException("This continuation has already completed successfully.");
                case FAILED -> throw new IllegalContinuationStateException("This continuation has failed and must be discarded.");
                case LOCKED -> throw new IllegalContinuationStateException("You can't resume a continuation while it is being serialized or deserialized.");
                // this is racy so ensure we have a general error message in those case
                default -> throw new IllegalContinuationStateException("Only new or suspended continuation can be resumed");
            }
        }
    }

    @Override
    public synchronized StackTraceElement[] getRecordedFrames() {
        State currentState = lock();
        try {
            ensureDematerialized();
            return getRecordedFrames0();
        } finally {
            unlock(currentState);
        }
    }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Catch ContinuationExecutionException, extract the original cause, and abandon the continuation.
  2. Check getState()/failure handling before any retry; FAILED is terminal.
  3. Re-run the workload by creating a new Continuation, not by resuming the failed one.
  4. Exclude resume() from blanket retry policies.

Example fix

// before
try {
    continuation.resume();
} catch (Exception e) {
    continuation.resume(); // FAILED -> IllegalContinuationStateException
}

// after
try {
    continuation.resume();
} catch (ContinuationExecutionException e) {
    log.error("workload failed", e.getCause());
    // discard continuation; optionally build a new one and re-run the workload
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (continuation.getState() == Continuation.State.FAILED) {
    // terminal: discard and optionally rebuild from scratch
}

Try / catch

try {
    continuation.resume();
} catch (ContinuationExecutionException e) {
    log.error("workload failed", e.getCause());
    // discard; never resume again
}

Prevention

When it happens

Trigger: The entry point threw an exception, the continuation transitioned to FAILED, and code retries with resume(); catch blocks around resume() that swallow the ContinuationExecutionException and then call resume() again.

Common situations: Generic retry-on-exception wrappers (resilience4j-style) applied to resume(); supervisors that attempt to restart 'stuck' continuations.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/594729112268e868. Report an issue: GitHub.