{"record":{"id":"10da6b2697723a39","repo":"oracle/graal","slug":"you-can-t-resume-an-already-executing-continuation","errorCode":null,"errorMessage":"You can't resume an already executing continuation.","messagePattern":"You can't resume an already executing continuation\\.","errorType":"exception","errorClass":"IllegalContinuationStateException","httpStatus":null,"severity":"error","filePath":"espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java","lineNumber":376,"sourceCode":"            try {\n                return start0();\n            } finally {\n                clearExclusiveOwner();\n            }\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);","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/org.graalvm.continuations/src/org/graalvm/continuations/ContinuationImpl.java#L358-L394","documentation":"IllegalContinuationStateException raised by resume() when the continuation's state is RUNNING. A continuation that is currently executing on a thread cannot be re-entered; resume is only legal from the NEW or SUSPENDED states. This typically means resume() was called re-entrantly or concurrently.","triggerScenarios":"Calling resume() from inside the continuation's own entry point (self-resume); a second thread calling resume() while the first is still inside resume(); resume() invoked from a listener/callback fired during continuation execution.","commonSituations":"Wrapping resume() in a scheduler that retries on failure without waiting for completion; recursive task schedulers where a task triggers its own continuation; missing synchronization around a shared continuation object.","solutions":["Only call resume() after the previous resume() returned (i.e. after suspend or completion).","Serialize access to the continuation with a per-continuation lock or single-owner thread.","If self-resume is intended, restructure so the entry point suspends instead of re-resuming.","Check getState() == SUSPENDED before resume as a cheap pre-condition (still race-prone; keep the try-catch)."],"exampleFix":"// before\n// thread A and thread B both do:\ncontinuation.resume(); // one hits 'already executing'\n\n// after\nsynchronized (continuation) {\n    if (continuation.getState() == Continuation.State.SUSPENDED) {\n        continuation.resume();\n    }\n}","handlingStrategy":"validation","validationCode":"if (continuation.getState() != Continuation.State.SUSPENDED && continuation.getState() != Continuation.State.NEW) {\n    // do not resume now\n}","typeGuard":null,"tryCatchPattern":"try {\n    continuation.resume();\n} catch (IllegalContinuationStateException e) {\n    // another thread is running it; back off and reschedule\n}","preventionTips":["Give each continuation a single owning thread/executor.","Never call resume() from inside the continuation body.","Guard resume with an external lock when shared."],"tags":["continuations","illegal-state","concurrency","graalvm","truffle"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}