{"record":{"id":"132329c5bf616fba","repo":"oracle/graal","slug":"erroneous-deserialization-of-suspendcapability-nr","errorCode":null,"errorMessage":"Erroneous deserialization of SuspendCapability.\\nread object was not a continuation:","messagePattern":"Erroneous deserialization of SuspendCapability\\.\\\\nread object was not a continuation:","errorType":"exception","errorClass":"FormatVersionException","httpStatus":null,"severity":"error","filePath":"espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/SuspendCapability.java","lineNumber":106,"sourceCode":"    }\n\n    @Override\n    void writeObjectExternal(ObjectOutput out) throws IOException {\n        out.writeObject(continuation);\n    }\n\n    static SuspendCapability readObjectExternal(ObjectInput in, Consumer<SuspendCapability> registerFreshObject) throws IOException, ClassNotFoundException {\n        if (!Continuation.isSupported()) {\n            throw new UnsupportedOperationException(\"This VM does not support continuations.\");\n        }\n        SuspendCapability suspend = new SuspendCapability();\n        registerFreshObject.accept(suspend);\n        Object obj = in.readObject();\n        if (obj instanceof ContinuationImpl) {\n            suspend.continuation = (ContinuationImpl) obj;\n            return suspend;\n        }\n        throw new FormatVersionException(\"Erroneous deserialization of SuspendCapability.\\n\" +\n                        \"read object was not a continuation:\" + obj);\n    }\n\n    // endregion internals\n}\n","sourceCodeStart":88,"sourceCodeEnd":112,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/SuspendCapability.java#L88-L112","documentation":"FormatVersionException from SuspendCapability.readObjectExternal: the first object read from the stream was expected to be the associated ContinuationImpl, but instanceof ContinuationImpl failed. A SuspendCapability is serialized as exactly one object — its continuation — so anything else (null, a different type, or a misordered read) means the stream was not written by the matching write path or was corrupted/reordered in transit.","triggerScenarios":"Deserializing a SuspendCapability from a stream where the first object is not a ContinuationImpl: wrong stream offset, a payload for a different type, or a custom serialization framework that reorders fields.","commonSituations":"Custom ObjectInput implementations (polyglot or off-heap serializers) that reorder writes; passing a raw ContinuationSerializable stream segment at the wrong offset; payload corruption or truncation that makes readObject decode a different object.","solutions":["Ensure the payload was produced by the library's own writeObjectExternal (capability and continuation in the same graph) and is consumed by the matching read path.","Do not manually slice or reassemble serialized continuation byte streams; treat them as opaque blobs.","Print/debug the offending object (it is included in the message via toString) to identify which serializer wrote the wrong thing.","If a custom serialization framework intercepts the graph, disable replacement/resolution for continuation objects."],"exampleFix":"// before\nObject first = in.readObject(); // custom reader reads fields out of order\nSuspendCapability cap = SuspendCapability.readObjectExternal(in, reg);\n\n// after\n// keep the pair opaque: serialize the whole continuation graph via the public API\nbyte[] blob = Continuation.serialize(cont); // capability + continuation stay consistent\nContinuation restored = Continuation.deserialize(blob, loader);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { Object o = ContinuationSerializable.readObjectExternal(type, in, loader, reg); } catch (FormatVersionException e) { /* message shows the offending object: stream misassembled; discard */ }","preventionTips":["Treat serialized continuation graphs as opaque; never slice, reorder, or re-emit their bytes.","Disable object replacement/resolveObject in custom ObjectInput streams for continuation types.","Use the object identity from the message to find which serializer wrote the wrong shape."],"tags":["serialization","continuations","corrupt-stream","deserialization"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}