eclipse-vertx/vert.x · error · IllegalStateException

A push response cannot promise another push

Error message

A push response cannot promise another push

What it means

In HTTP/2 server push, the response sent for a pushed resource is itself an HttpServerResponseImpl flagged with push=true. Such a push response must not initiate further pushes (the spec forbids nested push promises), so push() on a push response throws IllegalStateException.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerResponseImpl.java:721

  @Override
  public long streamId() {
    return stream.id();
  }

  @Override
  public Future<Void> reset(long code) {
    return stream.writeReset(code);
  }

  @Override
  public Future<Boolean> cancel() {
    return stream.cancel();
  }

  @Override
  public Future<HttpServerResponse> push(HttpMethod method, HostAndPort authority, String path, MultiMap headers) {
    if (push) {
      throw new IllegalStateException("A push response cannot promise another push");
    }
    if (authority == null) {
      authority = requestAuthority;
    }
    synchronized (conn) {
      checkValid();
    }
    HostAndPort h = authority;
    Future<HttpServerStream> fut = stream.sendPush(authority, method, headers, path, stream.priority());
    return fut.map(pushStream -> {
      HttpServerResponseImpl response = new HttpServerResponseImpl(pushStream, context, true);
      response.requestMethod = method;
      response.requestAuthority = h;
      PushStreamHandler push = new PushStreamHandler(pushStream, response, context);
      push.init();
      return push.response;
    });
  }

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Only call push() on the original request's response, never inside the pushHandler's response
  2. Track whether a response is a push response and skip push logic for it
  3. Refactor resource-pushing code to check a flag before invoking push()

Example fix

// before
response.pushHandler(pushedRes -> {
  pushedRes.push(method, host, path); // IllegalStateException: nested push
});

// after
response.pushHandler(pushedRes -> {
  // serve the pushed resource, do NOT push again from it
  pushedRes.putHeader("Content-Type", "text/plain").end("pushed");
});
Defensive patterns

Strategy: validation

Validate before calling

// only push from the original response, never inside pushHandler
if (!isPushResponse(response)) {
  response.push(method, authority, path);
}

Try / catch

try {
  response.push(method, authority, path);
} catch (IllegalStateException e) {
  // this is a push response; nested push not allowed
}

Prevention

When it happens

Trigger: Calling response.push(...) on the HttpServerResponse obtained from the pushHandler (the response you send for a pushed resource), instead of on the original client-initiated response.

Common situations: Recursive push logic that pushes resources found in any response, including push responses; generic handlers applied to push responses that unconditionally call push().

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