eclipse-vertx/vert.x · error · IllegalStateException

Response head already sent

Error message

Response head already sent

What it means

Thrown by checkHeadWritten() when an operation that must occur before the HTTP head is sent (e.g. setting status code or headers via setStatusCode/putHeader in paths that validate head state) is attempted after headWritten == true. Message: "Response head already sent".

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerResponse.java:673

      closedHandler = this.closeHandler;
    }
    if (exceptionHandler != null) {
      context.dispatch(HttpUtils.CONNECTION_CLOSED_EXCEPTION, exceptionHandler);
    }
    if (closedHandler != null) {
      context.dispatch(null, closedHandler);
    }
  }

  private void checkValid() {
    if (written) {
      throw new IllegalStateException(RESPONSE_WRITTEN);
    }
  }

  private void checkHeadWritten() {
    if (headWritten) {
      throw new IllegalStateException("Response head already sent");
    }
  }

  private void prepareHeaders(long contentLength) {
    if (version == HttpVersion.HTTP_1_0 && keepAlive) {
      headers.set(HttpHeaders.CONNECTION, HttpHeaders.KEEP_ALIVE);
    } else if (version == HttpVersion.HTTP_1_1 && !keepAlive) {
      headers.set(HttpHeaders.CONNECTION, HttpHeaders.CLOSE);
    }
    if (head || status == HttpResponseStatus.NOT_MODIFIED) {
      // For HEAD request or NOT_MODIFIED response
      // don't set automatically the content-length
      // and remove the transfer-encoding
      headers.remove(HttpHeaders.TRANSFER_ENCODING);
    } else {
      // Set content-length header automatically
      if (contentLength >= 0 && !headers.contains(HttpHeaders.CONTENT_LENGTH) && !headers.contains(HttpHeaders.TRANSFER_ENCODING)) {
        headers.set(HttpHeaders.CONTENT_LENGTH, HttpUtils.positiveLongToString(contentLength));

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Set status code and headers before any write/end call
  2. Check response.headWritten() before mutating status/headers
  3. If the head is already out, communicate the failure through the body or close the connection instead

Example fix

// before
response.write("partial");
response.setStatusCode(500);
// after
response.setStatusCode(500);
response.write("partial");
Defensive patterns

Strategy: validation

Validate before calling

if (!response.headWritten()) {
  response.setStatusCode(500);
}

Try / catch

try { response.setStatusCode(code); } catch (IllegalStateException e) { /* head already sent */ }

Prevention

When it happens

Trigger: Calling setStatusCode(), set status/headers-changing APIs guarded by checkHeadWritten(), or sendFile-like paths after the head was flushed by an earlier write()/end()/writeHead().

Common situations: Error-handling code trying to change the status code after a partial body was already written; middleware setting status after the handler flushed headers.

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/10a394bee3945849. Report an issue: GitHub.