{"record":{"id":"574a3ca297964562","repo":"quarkusio/quarkus","slug":"request-already-suspended","errorCode":null,"errorMessage":"Request already suspended","messagePattern":"Request already suspended","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java","lineNumber":223,"sourceCode":"        @Override\n        public ResteasyAsynchronousResponse getAsyncResponse() {\n            return asyncResponse;\n        }\n\n        @Override\n        public ResteasyAsynchronousResponse suspend() throws IllegalStateException {\n            return suspend(-1);\n        }\n\n        @Override\n        public ResteasyAsynchronousResponse suspend(long millis) throws IllegalStateException {\n            return suspend(millis, TimeUnit.MILLISECONDS);\n        }\n\n        @Override\n        public ResteasyAsynchronousResponse suspend(long time, TimeUnit unit) throws IllegalStateException {\n            if (wasSuspended) {\n                throw new IllegalStateException(\"Request already suspended\");\n            }\n            wasSuspended = true;\n            return asyncResponse;\n        }\n\n        @Override\n        public void complete() {\n            if (wasSuspended && asyncResponse != null)\n                asyncResponse.complete();\n        }\n\n        @Override\n        public CompletionStage<Void> executeAsyncIo(CompletionStage<Void> f) {\n            // check if this CF is already resolved\n            CompletableFuture<Void> ret = f.toCompletableFuture();\n            // if it's not resolved, we may need to suspend\n            if (!ret.isDone() && !isSuspended()) {\n                suspend();","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpRequest.java#L205-L241","documentation":"VertxHttpRequest.suspend(time, unit) implements the JAX-RS AsyncResponse suspend contract: a request can be suspended only once. If suspend is called when wasSuspended is already true it throws IllegalStateException(\"Request already suspended\"), mirroring the Servlet spec's rule against double suspension.","triggerScenarios":"Calling request.suspend(...) / asyncResponse-based suspension twice on the same request — e.g. calling suspend() after a previous suspend() or suspend(long, TimeUnit) without resuming/completing the prior AsyncResponse, or invoking suspend again in a filter and again in the resource.","commonSituations":"Manual async JAX-RS code where both a ContainerRequestFilter and the resource call suspend; retry logic that re-suspends after resume was not called; copying Servlet-style suspend code that allowed re-arm in another container.","solutions":["Call suspend exactly once per request; track your own boolean or rely on the AsyncResponse returned from the first call.","Always resume()/cancel() the AsyncResponse when done so downstream logic doesn't attempt re-suspension.","If you need a timeout extension, use setTimeout(long, TimeUnit) instead of calling suspend again.","Move suspension to a single place (filter or resource, not both)."],"exampleFix":"// before\nrequest.suspend();\nrequest.suspend(5, TimeUnit.SECONDS); // IllegalStateException\n\n// after\nAsyncResponse async = request.suspend(5, TimeUnit.SECONDS);\nasync.setTimeout(10, TimeUnit.SECONDS); // adjust timeout instead of re-suspending","handlingStrategy":"validation","validationCode":"if (asyncResponse != null && !asyncResponse.isDone() && !asyncResponse.isCancelled() && suspended) {\n    throw new IllegalStateException(\"suspend() already called for this request\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    asyncResp = request.suspend(5, TimeUnit.SECONDS);\n} catch (IllegalStateException e) {\n    // already suspended: reuse existing asyncResp instead of re-suspending\n    asyncResp.setTimeout(5, TimeUnit.SECONDS);\n}","preventionTips":["Suspend exactly once per request, in a single well-defined place.","Always resume() or cancel() the AsyncResponse when finished.","Use setTimeout() to change timeouts instead of calling suspend again.","Track suspension state with your own flag in filters that may run multiple times."],"tags":["jaxrs","async","illegal-state"],"backgroundTag":"request-already-suspended","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}