eclipse-vertx/vert.x · error · IllegalStateException
Request has already been read
Error message
Request has already been read
What it means
HttpServerRequestImpl.checkEnded() guards all request-reading configuration methods. Once the request body has been fully read (ended == true), mutating stream settings such as handlers, pause/resume, fetch, or enabling multipart parsing is no longer meaningful, so the library throws IllegalStateException to signal the request is consumed.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerRequestImpl.java:239
if (handler != null) {
handler.handleEnd();
}
}
public void handleReset(long errorCode) {
boolean notify;
synchronized (connection) {
notify = !ended;
}
if (notify) {
notifyException(new StreamResetException(errorCode));
}
response.handleReset(errorCode);
}
private void checkEnded() {
if (ended) {
throw new IllegalStateException("Request has already been read");
}
}
@Override
public HttpMethod method() {
return method;
}
@Override
public Object metric() {
return stream.metric();
}
@Override
public ContextInternal context() {
return context;
}
View on GitHub (pinned to fb308bd8c3)
Solutions
- Configure all handlers and setExpectMultipart/uploadHandler immediately when the request arrives, before reading the body
- If you need the full body first, don't reconfigure afterwards — operate on the already-read Buffer instead
- Restructure async flows so request stream configuration happens synchronously in the request handler, before any await/callback continuation
- Wrap configuration in a check: only call these APIs if the request hasn't ended (track it yourself, since ended is private)
Example fix
// before
request.body().onSuccess(body -> {
request.setExpectMultipart(true); // IllegalStateException: already read
});
// after
request.setExpectMultipart(true); // configure before reading
request.uploadHandler(upload -> { /* ... */ });
request.body().onSuccess(body -> { /* handle decoded body */ }); Defensive patterns
Strategy: validation
Validate before calling
// Configure everything before reading the body:
if (!requestHandled) {
request.setExpectMultipart(true);
request.uploadHandler(u -> {});
} Try / catch
try {
request.setExpectMultipart(true);
} catch (IllegalStateException e) {
// request already fully read; handle body elsewhere
} Prevention
- Set all request stream config at the top of the request handler
- Never consume the body (body()/endHandler) before configuring multipart/handlers
- Keep body reading in one place to avoid hidden early consumption
When it happens
Trigger: Calling handler(), pause(), fetch(), endHandler(), setExpectMultipart(true), or uploadHandler() after the request body has already been fully read (end() received / body() or similar already consumed the stream).
Common situations: Setting setExpectMultipart(true) inside an endHandler or after awaiting request.body(); configuring handlers after routing middleware already consumed the body; async code that resumes configuration after the body completed.
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
- Not a streaming upload
- Response head already sent
- Response has already been written
- size must be > 0
- maxExecuteTime must be > 0
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/a5dc72c4c34435ad.
Report an issue: GitHub.