quarkusio/quarkus · error · RuntimeException
Async can only be started once
Error message
Async can only be started once
What it means
ResteasyReactiveRequestContext.setAsyncResponse attaches the JAX-RS AsyncResponse to the request context. Each request may start asynchronous processing only once; if an AsyncResponse is already attached, this RuntimeException is thrown to signal a duplicate suspension of the same request.
Source
Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java:674
}
return genericReturnType;
}
public ResteasyReactiveRequestContext setGenericReturnType(Type genericReturnType) {
this.genericReturnType = genericReturnType;
return this;
}
private static final String ASYNC_RESPONSE_PROPERTY_KEY = AbstractResteasyReactiveContext.CUSTOM_RR_PROPERTIES_PREFIX
+ "AsyncResponse";
public AsyncResponseImpl getAsyncResponse() {
return (AsyncResponseImpl) getProperty(ASYNC_RESPONSE_PROPERTY_KEY);
}
public ResteasyReactiveRequestContext setAsyncResponse(AsyncResponseImpl asyncResponse) {
if (getAsyncResponse() != null) {
throw new RuntimeException("Async can only be started once");
}
setProperty(ASYNC_RESPONSE_PROPERTY_KEY, asyncResponse);
return this;
}
public ReaderInterceptor[] getReaderInterceptors() {
return readerInterceptors;
}
public ResteasyReactiveRequestContext setReaderInterceptors(ReaderInterceptor[] readerInterceptors) {
this.readerInterceptors = readerInterceptors;
return this;
}
public WriterInterceptor[] getWriterInterceptors() {
return writerInterceptors;
}
View on GitHub (pinned to e1c734241f)
Solutions
- Ensure a single request invokes the async resource method only once; resume the existing AsyncResponse instead of creating a new one.
- If forwarding internally, complete the original request rather than re-entering the async endpoint with the same context.
- Check custom filters/interceptors for accidental re-dispatch to the same async resource.
- In tests, create a fresh request context per invocation instead of reusing the context object.
Example fix
// before asyncResponse.resume(result); service.callAgain(context); // re-enters setAsyncResponse // after asyncResponse.resume(result); // single suspension; resume the existing response only
Defensive patterns
Strategy: type-guard
Validate before calling
if (context.getAsyncResponse() != null) {
// async already started: resume the existing response instead of suspending again
return;
} Type guard
AsyncResponseImpl existing = context.getAsyncResponse();
if (existing != null) {
// already suspended: reuse `existing` rather than calling setAsyncResponse
return existing;
} Try / catch
try {
context.setAsyncResponse(asyncResponse);
} catch (RuntimeException e) {
if ("Async can only be started once".equals(e.getMessage())) {
log.debug("Request already suspended; reusing existing AsyncResponse");
} else {
throw e;
}
} Prevention
- Invoke async resource methods exactly once per request.
- Do not re-dispatch or forward requests into the same async endpoint.
- Avoid reusing ResteasyReactiveRequestContext across invocations in tests.
- Review filters/interceptors for accidental re-entry into async resources.
When it happens
Trigger: A resource method returning AsyncResponse (or using @Suspended AsyncResponse) is invoked while the same request context already holds an async response — e.g. calling the async resource method twice within one request, or dispatching the same context through a filter/interceptor that re-enters the async method.
Common situations: Re-invoking a suspended resource method from a background thread; forwarding/rewriting a request to an async endpoint internally; misconfigured filters that re-dispatch; frameworks or tests reusing a request context across invocations.
Related errors
- Unhandled async return type
- Cannot load async task class: {taskClass}
- Parameter: ${i} of the constructor of class '${resourceDotNa
- Parameter: ${i} of the constructor of class '${resourceDotNa
- Unsupported type '${jaxRSAnnotationOfParam.name()}' used as
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/d4fbe0b78cc8f5da.
Report an issue: GitHub.