{"record":{"id":"40ea2379c10a4722","repo":"oracle/graal","slug":"could-not-deserialize-a-continuation-object","errorCode":null,"errorMessage":"Could not deserialize a continuation object.","messagePattern":"Could not deserialize a continuation object\\.","errorType":"exception","errorClass":"FormatVersionException","httpStatus":null,"severity":"error","filePath":"espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationSerializable.java","lineNumber":101,"sourceCode":"     *\n     * @param type The type of the object to deserialize. Can be either {@link Continuation} or\n     *            {@link SuspendCapability}.\n     * @param loader The class loader that will be used to deserialize the stack record. If\n     *            {@code null}, the {@link Thread#getContextClassLoader() current thread context\n     *            class loader} will be used.\n     * @param registerFreshObject A callback to the serialization framework. Intended to be used to\n     *            register the in-construction object, so the framework may handle object graph\n     *            cycles.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends ContinuationSerializable> T readObjectExternal(Class<T> type, ObjectInput in, ClassLoader loader, Consumer<T> registerFreshObject)\n                    throws IOException, ClassNotFoundException {\n        if (Continuation.class.isAssignableFrom(type)) {\n            return (T) ContinuationImpl.readObjectExternal(in, loader, (Consumer<Continuation>) registerFreshObject);\n        } else if (SuspendCapability.class.isAssignableFrom(type)) {\n            return (T) SuspendCapability.readObjectExternal(in, (Consumer<SuspendCapability>) registerFreshObject);\n        } else {\n            throw new FormatVersionException(\"Could not deserialize a continuation object.\");\n        }\n    }\n\n    // region internals\n    abstract void writeObjectExternal(ObjectOutput out) throws IOException;\n}\n","sourceCodeStart":83,"sourceCodeEnd":108,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationSerializable.java#L83-L108","documentation":"Thrown as FormatVersionException by the static bridge ContinuationSerializable.readObjectExternal when the requested type is neither a subtype of Continuation nor of SuspendCapability. This dispatcher is the deserialization entry for the whole 'continuation object' family; it refuses types outside that hierarchy because no reader exists for them.","triggerScenarios":"Calling ContinuationSerializable.readObjectExternal(MyType.class, in, loader, reg) where MyType does not extend Continuation or SuspendCapability; passing an interface or Class<?> obtained reflectively that looks like a serializable continuation but is not.","commonSituations":"Generic deserialization frameworks (Kryo/Java serialization bridges, polyglot serializers) that route arbitrary classes through this entry point; copy-pasting a readObjectExternal call from continuation code into a custom serializable class.","solutions":["Only route classes that extend Continuation (ContinuationImpl) or SuspendCapability through this API.","For your own serializable types, implement readObjectExternal/writeObjectExternal on a ContinuationSerializable subclass instead of calling the static bridge.","Validate the class before calling: Continuation.class.isAssignableFrom(type) || SuspendCapability.class.isAssignableFrom(type).","If the type should be a continuation but is not, check that your class extends the public Continuation API class from org.graalvm.continuations, not a look-alike from another package."],"exampleFix":"// before\nT obj = ContinuationSerializable.readObjectExternal(type, in, loader, this::register);\n\n// after\nif (!Continuation.class.isAssignableFrom(type) && !SuspendCapability.class.isAssignableFrom(type)) {\n    throw new IllegalArgumentException(type + \" is not deserializable by the continuation framework\");\n}\nT obj = ContinuationSerializable.readObjectExternal(type, in, loader, this::register);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isContinuationSerializableType(Class<?> t) {\n    return Continuation.class.isAssignableFrom(t) || SuspendCapability.class.isAssignableFrom(t);\n}","tryCatchPattern":null,"preventionTips":["Route only Continuation/SuspendCapability subtypes through ContinuationSerializable.readObjectExternal.","For your own types, subclass ContinuationSerializable and implement its hooks instead of using the static bridge.","In generic frameworks, dispatch on type before calling the bridge."],"tags":["serialization","continuations","type-dispatch","api-misuse"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}