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

  1. Ensure the payload was created exclusively via Continuation.serialize on a suspended or unstarted continuation — never patch the stream by hand.
  2. Remove any custom ObjectInputStream resolveObject/class-replacement logic that could remap the State enum constant.
  3. Catch IllegalContinuationStateException on deserialize and discard the payload as corrupt rather than retrying it.
  4. 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

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


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