oracle/graal · error · IllegalContinuationStateException
You can't resume a continuation while it is being serialized
Error message
You can't resume a continuation while it is being serialized or deserialized.
What it means
IllegalContinuationStateException raised by resume() while the continuation is locked (state LOCKED), i.e. another thread is currently serializing or deserializing it. The state machine blocks resume during I/O on the continuation's frames to prevent corruption.
Source
Thrown at espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java:380
}
} 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);
}
}
@OverrideView on GitHub (pinned to a66e9ccd1d)
Solutions
- Externalize synchronization: hold a lock over the whole serialize-or-resume decision so the two never overlap.
- Suspend the continuation before serializing; only resume after serialization finishes.
- Route all state transitions (resume/serialize/deserialize) through a single owner/actor.
- On IllegalContinuationStateException with LOCKED, back off and retry after the I/O completes.
Example fix
// before
// thread A: out.writeObject(continuation);
// thread B: continuation.resume(); // may hit LOCKED
// after
Object lock = new Object();
synchronized (lock) {
out.writeObject(continuation);
} // resume only after serialization is done, under the same lock Defensive patterns
Strategy: retry
Validate before calling
if (continuation.getState() == Continuation.State.LOCKED) {
// serialization in progress; wait for it to finish before resume
} Try / catch
try {
continuation.resume();
} catch (IllegalContinuationStateException e) {
if (continuation.getState() == Continuation.State.LOCKED) {
// back off, retry after serialization completes
}
} Prevention
- Serialize and resume under one external lock.
- Suspend before serializing; resume only after I/O completes.
- Centralize state transitions through a single owner.
When it happens
Trigger: Calling resume() concurrently with writeObject/readObject on the same continuation from different threads; deserialization code that resumes before the read completes; checkpointing (serialization) racing the scheduler's resume.
Common situations: Persistence/checkpoint layers serializing continuations while a worker thread resumes them; missing coordination between the serializer thread and execution thread.
Related errors
- You can't resume an already executing continuation.
- Only new or suspended continuation can be resumed
- Suspend capabilities can only be used inside a continuation.
- You cannot serialize a continuation whilst it's running, as
- This continuation has already completed successfully.
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/fe47986bc8cf6de9.
Report an issue: GitHub.