oracle/graal · error · FormatVersionException
Erroneous deserialization of SuspendCapability.\nread object
Error message
Erroneous deserialization of SuspendCapability.\nread object was not a continuation:
What it means
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.
Source
Thrown at espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/SuspendCapability.java:106
}
@Override
void writeObjectExternal(ObjectOutput out) throws IOException {
out.writeObject(continuation);
}
static SuspendCapability readObjectExternal(ObjectInput in, Consumer<SuspendCapability> registerFreshObject) throws IOException, ClassNotFoundException {
if (!Continuation.isSupported()) {
throw new UnsupportedOperationException("This VM does not support continuations.");
}
SuspendCapability suspend = new SuspendCapability();
registerFreshObject.accept(suspend);
Object obj = in.readObject();
if (obj instanceof ContinuationImpl) {
suspend.continuation = (ContinuationImpl) obj;
return suspend;
}
throw new FormatVersionException("Erroneous deserialization of SuspendCapability.\n" +
"read object was not a continuation:" + obj);
}
// endregion internals
}
View on GitHub (pinned to a66e9ccd1d)
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.
Example fix
// before Object first = in.readObject(); // custom reader reads fields out of order SuspendCapability cap = SuspendCapability.readObjectExternal(in, reg); // after // keep the pair opaque: serialize the whole continuation graph via the public API byte[] blob = Continuation.serialize(cont); // capability + continuation stay consistent Continuation restored = Continuation.deserialize(blob, loader);
Defensive patterns
Strategy: try-catch
Try / catch
try { Object o = ContinuationSerializable.readObjectExternal(type, in, loader, reg); } catch (FormatVersionException e) { /* message shows the offending object: stream misassembled; discard */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Missing this in serialized frame.
- Illegal serialized continuation is in running state.
- Failed to deserialize frame for %s.%s().
- Unexpected kind:
- Setting ihashcode of an object whose ihashcode is already in
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/132329c5bf616fba.
Report an issue: GitHub.