denoland/deno · error · Error

ERR_HTTP2_NESTED_PUSH

ERR_HTTP2_NESTED_PUSH

Error message

A push stream cannot initiate another push stream.

What it means

pushStream() rejects nested pushes: streams initiated by a client have odd IDs and server-initiated push streams have even IDs, and the check this[kID] % 2 === 0 throws ERR_HTTP2_NESTED_PUSH when the initiating stream is itself a push stream ('A push stream cannot initiate another push stream'). This matches the HTTP/2 spec, where only the request stream may push.

Source

Thrown at ext/node/polyfills/http2.ts:3090

  }

  // True if the remote peer accepts push streams
  get pushAllowed() {
    return !this.destroyed &&
      !this.closed &&
      !this.session.closed &&
      !this.session.destroyed &&
      this[kSession].remoteSettings.enablePush;
  }

  // Create a push stream, call the given callback with the created
  // Http2Stream for the push stream.
  pushStream(headers, options, callback) {
    if (!this.pushAllowed) {
      throw new ERR_HTTP2_PUSH_DISABLED();
    }
    if (this[kID] % 2 === 0) {
      throw new ERR_HTTP2_NESTED_PUSH();
    }

    const session = this[kSession];

    debugStreamObj(this, "initiating push stream");

    this[kUpdateTimer]();

    if (typeof options === "function") {
      callback = options;
      options = undefined;
    }

    validateFunction(callback, "callback");

    assertIsObject(options, "options");
    options = { ...options };
    options.endStream = !!options.endStream;

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Only initiate pushStream from the original request stream (odd ID); never from inside a push callback
  2. Pass the original stream (or its session + explicit flag) through your helper so pushes always root at the request
  3. Flatten dependency graphs into one batch of pushes from the request stream instead of chaining

Example fix

// before
stream.pushStream(indexHeaders, (err, pushStream) => {
  pushStream.pushStream(cssHeaders, cb); // throws ERR_HTTP2_NESTED_PUSH
});

// after
stream.pushStream(indexHeaders, cb);
stream.pushStream(cssHeaders, cb); // both rooted at the request stream
Defensive patterns

Strategy: validation

Validate before calling

const isRequestStream = (s: Http2Stream): boolean => s.id % 2 === 1;
if (isRequestStream(stream)) {
  stream.pushStream(headers, onPush); // only request streams may push
}

Type guard

const isRequestStream = (s: Http2Stream & { id: number }): boolean =>
  s.id % 2 === 1;

Try / catch

try {
  pushStream.pushStream(headers, onPush);
} catch (err) {
  if ((err as NodeJS.ErrnoException).code === 'ERR_HTTP2_NESTED_PUSH') {
    // re-root the push on the original request stream instead
  } else throw err;
}

Prevention

When it happens

Trigger: Calling pushStream inside the callback of a previous pushStream — e.g. a generic 'push associated resources' helper applied recursively to the stream it just received; passing the pushed stream from the pushStream callback into shared middleware that also pushes.

Common situations: Recursive asset-preload helpers (push index.html, then on the pushed stream push its CSS/JS); refactored request handlers that no longer distinguish the original request stream from a push stream; graph-walkers that push every discovered dependency.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/18782dc2dbfb0d59. Report an issue: GitHub.