eclipse-vertx/vert.x · error · IllegalStateException
Response already ended
Error message
Response already ended
What it means
HttpClientResponseImpl.checkEnded() throws this IllegalStateException when a stream handler (handler, endHandler, exceptionHandler, customFrameHandler, streamPriorityHandler) is registered after the response has already ended. Handlers cannot be attached to a completed stream.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientResponseImpl.java:219
@Override
public List<String> cookies() {
synchronized (conn) {
if (cookies == null) {
cookies = new ArrayList<>();
cookies.addAll(headers().getAll(HttpHeaders.SET_COOKIE));
if (trailers != null) {
cookies.addAll(trailers.getAll(HttpHeaders.SET_COOKIE));
}
}
return cookies;
}
}
/** Must be called within a {@code synchronized (conn)} block. */
private void checkEnded() {
if (ended != null) {
throw new IllegalStateException("Response already ended");
}
}
@Override
public HttpClientResponse handler(Handler<Buffer> handler) {
synchronized (conn) {
if (handler != null) {
checkEnded();
}
HttpEventHandler eventHandler = eventHandler(handler != null);
if (eventHandler != null) {
eventHandler.chunkHandler(handler);
}
return this;
}
}
@OverrideView on GitHub (pinned to fb308bd8c3)
Solutions
- Register all handlers immediately when the response arrives, before consuming the body
- Use the Future-based body()/end() API instead of registering handlers after completion
- Check whether the response already ended before attaching handlers (or wrap in a try-catch if unavoidable)
Example fix
// before
response.body().onSuccess(b -> { /* ... */ });
response.endHandler(v -> cleanup()); // throws: already ended
// after
response.endHandler(v -> cleanup());
response.body().onSuccess(b -> { /* ... */ }); Defensive patterns
Strategy: try-catch
Validate before calling
// attach handlers synchronously when the response is received, before any await on body() response.endHandler(v -> cleanup()); response.exceptionHandler(err -> log(err)); response.handler(buffer -> accumulate(buffer));
Try / catch
try {
response.streamPriorityHandler(p -> ...);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Response already ended")) {
// response finished; nothing to subscribe to
} else { throw e; }
} Prevention
- Register all response handlers immediately upon receipt
- Prefer Future-based body()/end() consumption over late handler registration
- Do not re-subscribe to a response object after it completed
When it happens
Trigger: Calling response.handler(...)/endHandler(...)/exceptionHandler(...) after the response body has been fully consumed or the response ended asynchronously, e.g. inside a callback that runs after body() completed.
Common situations: Registering handlers in a later async step (after await/end of body); retry logic re-subscribing to the same ended response object; wrapping libraries that attach handlers lazily.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Client is closed
- Request already complete
- Response has already been written
- Already started
- Event Bus is not started
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/cd5f5ef8b8f27701.
Report an issue: GitHub.