oracle/graal · error · FormatVersionException

Unsupported serialized continuation version: %s\nCurrent sup

Error message

Unsupported serialized continuation version: %s\nCurrent supported version: %s

What it means

Thrown as FormatVersionException while deserializing a continuation: the version number encoded in the stream header (bits FORMAT_SHIFT..FORMAT_MASK of the first byte) does not equal ContinuationImpl.FORMAT_VERSION. The library uses this single version number to gate the whole serialized shape (state enum, entry point, frame records), so a mismatch means the bytes cannot be interpreted safely. It exists to fail fast instead of misparsing an incompatible payload.

Source

Thrown at espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java:657

            throw new UnsupportedOperationException("This VM does not support continuations.");
        }
        ContinuationImpl continuation = new ContinuationImpl();
        registerFreshObject.accept(continuation);
        continuation.readObjectExternalImpl(in, loader);
        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;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Re-serialize the continuation with the exact same org.graalvm.continuations version that will deserialize it (pin the GraalVM version on both ends).
  2. If migration is required, resume and re-suspend the continuation under the old runtime, then re-serialize and migrate the new payload.
  3. Catch FormatVersionException and treat the payload as poison: drop it and rebuild the continuation from its logical starting point instead of retrying.
  4. Verify the stream was produced by Continuation.serialize and was not truncated/corrupted in transport (check length, checksum) before blaming the version.

Example fix

// before
Continuation c = Continuation.deserialize(bytes, loader);

// after
try {
    Continuation c = Continuation.deserialize(bytes, loader);
} catch (FormatVersionException e) {
    // payload from an incompatible library version: discard and rebuild
    log.warn("Stale continuation payload (version {}), rebuilding", e.getMessage());
    return buildFreshContinuation();
}
Defensive patterns

Strategy: try-catch

Try / catch

try { Continuation c = Continuation.deserialize(bytes, loader); } catch (FormatVersionException e) { /* payload incompatible: quarantine and rebuild, never retry same bytes */ }

Prevention

When it happens

Trigger: Calling Continuation.deserialize (or ContinuationImpl.readObjectExternalImpl) on bytes produced by a different release of org.graalvm.continuations where FORMAT_VERSION differs; manually crafting or truncating the stream so the header byte decodes to a wrong version.

Common situations: Continuations persisted to disk or sent over the wire between GraalVM builds (e.g. rolling upgrade from an older GraalVM to a newer one); test fixtures recorded with an older library version; mixing continuation classes from two different GraalVM distributions on one classpath.

Related errors


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