quarkusio/quarkus · error · IllegalStateException

Response already committed

Error message

Response already committed

What it means

VertxHttpResponse.reset() implements the servlet-style reset contract: once the response is committed (headers/status already sent to the wire), it cannot be reset. It throws IllegalStateException("Response already committed") to prevent mutating headers of a response the client has already started receiving.

Source

Thrown at extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxHttpResponse.java:115

        }
        response.setStatusCode(status);
        if (message != null) {
            response.end(message);
        } else {
            response.end();
        }
        committed = true;
    }

    @Override
    public boolean isCommitted() {
        return committed;
    }

    @Override
    public void reset() {
        if (committed) {
            throw new IllegalStateException("Response already committed");
        }
        outputHeaders.clear();
    }

    private void transformHeaders() {
        getOutputHeaders().forEach(this::transformHeadersList);
    }

    private void transformHeadersList(final String key, final List<Object> valueList) {
        final MultiMap headers = response.headers();
        for (Object value : valueList) {
            if (value == null) {
                headers.add(key, "");
            } else {
                RuntimeDelegate.HeaderDelegate delegate = providerFactory.getHeaderDelegate(value.getClass());
                if (delegate != null) {
                    headers.add(key, delegate.toString(value));
                } else {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Only reset before writing any entity/status — restructure error handling so failures are detected before the first write/flush.
  2. Instead of reset after commit, log the issue and close the stream; the client already received the committed response.
  3. Use flush() deliberately and late: avoid premature flushing in mappers/interceptors so reset remains possible for error paths.

Example fix

// before
try {
    out.write(data);
} catch (Exception e) {
    response.reset(); // IllegalStateException: already committed
    response.setStatusCode(500);
}

// after
boolean ok = renderData(out); // validate/build first, write last
if (!ok) {
    response.setStatusCode(500); // nothing written yet, safe
}
Defensive patterns

Strategy: validation

Validate before calling

if (response.isCommitted()) {
    throw new IllegalStateException("Cannot reset: response already committed");
}
response.reset();

Type guard

boolean safeToReset(io.quarkus.resteasy.runtime.standalone.VertxHttpResponse resp) {
    return !resp.isCommitted();
}

Try / catch

try {
    response.reset();
} catch (IllegalStateException e) {
    LOG.warn("Response already committed; cannot reset — client received partial response");
}

Prevention

When it happens

Trigger: Calling response.reset() (directly or via getOutputStream side effects / container machinery) after the response was flushed or its headers written; e.g. an exception handler trying to reset the response to send a different status after partial output was committed.

Common situations: Custom error handling that attempts to change the status code after output started; exception mappers firing after the entity stream was flushed; frameworks that reset responses defensively in finally blocks.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/f76103cb5688e69e. Report an issue: GitHub.