{"record":{"id":"ca0d2b33c6f2c325","repo":"oracle/graal","slug":"illegal-serialized-continuation-is-in-running-stat","errorCode":null,"errorMessage":"Illegal serialized continuation is in running state.","messagePattern":"Illegal serialized continuation is in running state\\.","errorType":"exception","errorClass":"IllegalContinuationStateException","httpStatus":null,"severity":"error","filePath":"espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java","lineNumber":662,"sourceCode":"        return continuation;\n    }\n\n    synchronized void readObjectExternalImpl(ObjectInput in, ClassLoader loader) throws IOException, ClassNotFoundException {\n        State currentState = lock();\n        if (currentState == State.RUNNING) {\n            throw new IllegalContinuationStateException(\"You cannot serialize a continuation whilst it's running, as this would have unclear semantics. Please suspend first.\");\n        }\n        try {\n            // At this point, nothing is initialized\n            int header = in.readByte();\n            int version = (header >> FORMAT_SHIFT) & FORMAT_MASK;\n            if (version != FORMAT_VERSION) {\n                throw new FormatVersionException(version, FORMAT_VERSION);\n            }\n\n            currentState = (State) in.readObject();\n            if (currentState == State.RUNNING) {\n                throw new IllegalContinuationStateException(\"Illegal serialized continuation is in running state.\");\n            }\n            entryPoint = (ContinuationEntryPoint) in.readObject();\n\n            if (currentState == State.SUSPENDED) {\n                stackFrameHead = FrameRecordSerializer.forIn(version, in) //\n                                .withLoader(loader == null ? Thread.currentThread().getContextClassLoader() : loader) //\n                                .readRecord();\n            }\n            unlock(currentState);\n        } catch (Throwable e) {\n            // If any error occurs, leave the continuation as incomplete.\n            unlock(State.INCOMPLETE);\n            throw e;\n        }\n    }\n\n    // endregion Serialization\n","sourceCodeStart":644,"sourceCodeEnd":680,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java#L644-L680","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbyte[] payload = rewriteStateField(rawBytes); // custom stream surgery\nContinuation c = Continuation.deserialize(payload, loader);\n\n// after\n// no stream surgery: only serialize after suspend\nbyte[] payload = Continuation.serialize(suspendedContinuation);\nContinuation c = Continuation.deserialize(payload, loader);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { Continuation c = Continuation.deserialize(bytes, loader); } catch (IllegalContinuationStateException e) { /* stream claims RUNNING: corrupt/tampered payload, discard */ }","preventionTips":["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."],"tags":["serialization","continuations","corrupt-stream","state-machine"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}