{"record":{"id":"c14ecb8837263e27","repo":"eclipse-vertx/vert.x","slug":"a-push-response-cannot-promise-another-push","errorCode":null,"errorMessage":"A push response cannot promise another push","messagePattern":"A push response cannot promise another push","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerResponseImpl.java","lineNumber":721,"sourceCode":"  @Override\n  public long streamId() {\n    return stream.id();\n  }\n\n  @Override\n  public Future<Void> reset(long code) {\n    return stream.writeReset(code);\n  }\n\n  @Override\n  public Future<Boolean> cancel() {\n    return stream.cancel();\n  }\n\n  @Override\n  public Future<HttpServerResponse> push(HttpMethod method, HostAndPort authority, String path, MultiMap headers) {\n    if (push) {\n      throw new IllegalStateException(\"A push response cannot promise another push\");\n    }\n    if (authority == null) {\n      authority = requestAuthority;\n    }\n    synchronized (conn) {\n      checkValid();\n    }\n    HostAndPort h = authority;\n    Future<HttpServerStream> fut = stream.sendPush(authority, method, headers, path, stream.priority());\n    return fut.map(pushStream -> {\n      HttpServerResponseImpl response = new HttpServerResponseImpl(pushStream, context, true);\n      response.requestMethod = method;\n      response.requestAuthority = h;\n      PushStreamHandler push = new PushStreamHandler(pushStream, response, context);\n      push.init();\n      return push.response;\n    });\n  }","sourceCodeStart":703,"sourceCodeEnd":739,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerResponseImpl.java#L703-L739","documentation":"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.","triggerScenarios":"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.","commonSituations":"Recursive push logic that pushes resources found in any response, including push responses; generic handlers applied to push responses that unconditionally call push().","solutions":["Only call push() on the original request's response, never inside the pushHandler's response","Track whether a response is a push response and skip push logic for it","Refactor resource-pushing code to check a flag before invoking push()"],"exampleFix":"// before\nresponse.pushHandler(pushedRes -> {\n  pushedRes.push(method, host, path); // IllegalStateException: nested push\n});\n\n// after\nresponse.pushHandler(pushedRes -> {\n  // serve the pushed resource, do NOT push again from it\n  pushedRes.putHeader(\"Content-Type\", \"text/plain\").end(\"pushed\");\n});","handlingStrategy":"validation","validationCode":"// only push from the original response, never inside pushHandler\nif (!isPushResponse(response)) {\n  response.push(method, authority, path);\n}","typeGuard":null,"tryCatchPattern":"try {\n  response.push(method, authority, path);\n} catch (IllegalStateException e) {\n  // this is a push response; nested push not allowed\n}","preventionTips":["Never call push() from within a pushHandler's response","Guard push logic with an isPush flag","Keep push initiation in top-level request handlers only"],"tags":["http2","server-push","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}