oracle/graal · error · IllegalContinuationStateException
Illegal serialized continuation is in running state.
Error message
Illegal serialized continuation is in running state.
What it means
Thrown as IllegalContinuationStateException during ContinuationImpl.readObjectExternalImpl when the State enum object read from the stream equals State.RUNNING. A running continuation can never be legitimately serialized (the write path throws 'You cannot serialize a continuation whilst it's running'), so encountering RUNNING on the read path means the payload was not produced by the normal serialization path. It guards against resuming a continuation that claims to be actively executing on another thread.
Source
Thrown at espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java:662
return continuation;
}
synchronized void readObjectExternalImpl(ObjectInput in, ClassLoader loader) throws IOException, ClassNotFoundException {
State currentState = lock();
if (currentState == State.RUNNING) {
throw new IllegalContinuationStateException("You cannot serialize a continuation whilst it's running, as this would have unclear semantics. Please suspend first.");
}
try {
// At this point, nothing is initialized
int header = in.readByte();
int version = (header >> FORMAT_SHIFT) & FORMAT_MASK;
if (version != FORMAT_VERSION) {
throw new FormatVersionException(version, FORMAT_VERSION);
}
currentState = (State) in.readObject();
if (currentState == State.RUNNING) {
throw new IllegalContinuationStateException("Illegal serialized continuation is in running state.");
}
entryPoint = (ContinuationEntryPoint) in.readObject();
if (currentState == State.SUSPENDED) {
stackFrameHead = FrameRecordSerializer.forIn(version, in) //
.withLoader(loader == null ? Thread.currentThread().getContextClassLoader() : loader) //
.readRecord();
}
unlock(currentState);
} catch (Throwable e) {
// If any error occurs, leave the continuation as incomplete.
unlock(State.INCOMPLETE);
throw e;
}
}
// endregion Serialization
View on GitHub (pinned to a66e9ccd1d)
Solutions
- Ensure the payload was created exclusively via Continuation.serialize on a suspended or unstarted continuation — never patch the stream by hand.
- Remove any custom ObjectInputStream resolveObject/class-replacement logic that could remap the State enum constant.
- Catch IllegalContinuationStateException on deserialize and discard the payload as corrupt rather than retrying it.
- If you need to transport continuations, suspend first, serialize, and keep the bytes immutable end-to-end.
Example fix
// before byte[] payload = rewriteStateField(rawBytes); // custom stream surgery Continuation c = Continuation.deserialize(payload, loader); // after // no stream surgery: only serialize after suspend byte[] payload = Continuation.serialize(suspendedContinuation); Continuation c = Continuation.deserialize(payload, loader);
Defensive patterns
Strategy: try-catch
Try / catch
try { Continuation c = Continuation.deserialize(bytes, loader); } catch (IllegalContinuationStateException e) { /* stream claims RUNNING: corrupt/tampered payload, discard */ } Prevention
- Only suspend-then-serialize; never hand-edit serialized continuation bytes.
- Keep the payload immutable end-to-end (no custom ObjectInputStream resolveObject on continuation graphs).
- Treat RUNNING-on-read as a corruption signal, not a retryable state.
When it happens
Trigger: Deserializing a stream whose State object was hand-crafted, mutated, or produced by a custom ObjectOutput that wrote State.RUNNING; a corrupted or tampered payload where the state field decoded to RUNNING.
Common situations: Integration code that rewrites serialized continuation bytes (e.g. a custom ObjectInputStream replacement table or a bytecode-rewriting transport) accidentally swapping the State constant; fuzzing or security testing of the format.
Related errors
- Missing this in serialized frame.
- Unexpected kind:
- Erroneous deserialization of SuspendCapability.\nread object
- You can't resume a continuation while it is being serialized
- You cannot serialize a continuation whilst it's running, as
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/ca0d2b33c6f2c325.
Report an issue: GitHub.