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

  1. 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.
  2. Do not manually slice or reassemble serialized continuation byte streams; treat them as opaque blobs.
  3. Print/debug the offending object (it is included in the message via toString) to identify which serializer wrote the wrong thing.
  4. 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

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


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