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

  1. Configure all handlers and setExpectMultipart/uploadHandler immediately when the request arrives, before reading the body
  2. If you need the full body first, don't reconfigure afterwards — operate on the already-read Buffer instead
  3. Restructure async flows so request stream configuration happens synchronously in the request handler, before any await/callback continuation
  4. 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

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


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/a5dc72c4c34435ad. Report an issue: GitHub.