{"record":{"id":"fe47986bc8cf6de9","repo":"oracle/graal","slug":"you-can-t-resume-a-continuation-while-it-is-being","errorCode":null,"errorMessage":"You can't resume a continuation while it is being serialized or deserialized.","messagePattern":"You can't resume a continuation while it is being serialized or deserialized\\.","errorType":"exception","errorClass":"IllegalContinuationStateException","httpStatus":null,"severity":"error","filePath":"espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java","lineNumber":380,"sourceCode":"            }\n        } else if (updateState(State.SUSPENDED, State.RUNNING)) {\n            setExclusiveOwner();\n            try {\n                // We are ready to resume, make sure the VM has the most up-to-date frames\n                ensureDematerialized();\n                assert stackFrameHead == null;\n                return resume0();\n            } finally {\n                clearExclusiveOwner();\n            }\n        } else {\n            // illegal state for resume: neither suspended nor new\n            switch (state) {\n                case RUNNING -> throw new IllegalContinuationStateException(\"You can't resume an already executing continuation.\");\n                case COMPLETED ->\n                    throw new IllegalContinuationStateException(\"This continuation has already completed successfully.\");\n                case FAILED -> throw new IllegalContinuationStateException(\"This continuation has failed and must be discarded.\");\n                case LOCKED -> throw new IllegalContinuationStateException(\"You can't resume a continuation while it is being serialized or deserialized.\");\n                // this is racy so ensure we have a general error message in those case\n                default -> throw new IllegalContinuationStateException(\"Only new or suspended continuation can be resumed\");\n            }\n        }\n    }\n\n    @Override\n    public synchronized StackTraceElement[] getRecordedFrames() {\n        State currentState = lock();\n        try {\n            ensureDematerialized();\n            return getRecordedFrames0();\n        } finally {\n            unlock(currentState);\n        }\n    }\n\n    @Override","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java#L362-L398","documentation":"IllegalContinuationStateException raised by resume() while the continuation is locked (state LOCKED), i.e. another thread is currently serializing or deserializing it. The state machine blocks resume during I/O on the continuation's frames to prevent corruption.","triggerScenarios":"Calling resume() concurrently with writeObject/readObject on the same continuation from different threads; deserialization code that resumes before the read completes; checkpointing (serialization) racing the scheduler's resume.","commonSituations":"Persistence/checkpoint layers serializing continuations while a worker thread resumes them; missing coordination between the serializer thread and execution thread.","solutions":["Externalize synchronization: hold a lock over the whole serialize-or-resume decision so the two never overlap.","Suspend the continuation before serializing; only resume after serialization finishes.","Route all state transitions (resume/serialize/deserialize) through a single owner/actor.","On IllegalContinuationStateException with LOCKED, back off and retry after the I/O completes."],"exampleFix":"// before\n// thread A: out.writeObject(continuation);\n// thread B: continuation.resume(); // may hit LOCKED\n\n// after\nObject lock = new Object();\nsynchronized (lock) {\n    out.writeObject(continuation);\n} // resume only after serialization is done, under the same lock","handlingStrategy":"retry","validationCode":"if (continuation.getState() == Continuation.State.LOCKED) {\n    // serialization in progress; wait for it to finish before resume\n}","typeGuard":null,"tryCatchPattern":"try {\n    continuation.resume();\n} catch (IllegalContinuationStateException e) {\n    if (continuation.getState() == Continuation.State.LOCKED) {\n        // back off, retry after serialization completes\n    }\n}","preventionTips":["Serialize and resume under one external lock.","Suspend before serializing; resume only after I/O completes.","Centralize state transitions through a single owner."],"tags":["continuations","illegal-state","concurrency","serialization","graalvm","truffle"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}