denoland/deno · error · Error

ERR_HTTP2_PUSH_DISABLED

ERR_HTTP2_PUSH_DISABLED

Error message

HTTP/2 client has disabled push streams

What it means

Server-side stream.pushStream() requires stream.pushAllowed, which is true only when the stream and session are alive AND the client advertised enablePush in its remote settings. If the client connected with { settings: { enablePush: false } } (or the session/stream already closed), pushStream throws ERR_HTTP2_PUSH_DISABLED before creating anything.

Source

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

    this[kInit](id, handle);
    this[kProtocol] = headers[HTTP2_HEADER_SCHEME];
    this[kAuthority] = getAuthority(headers);
  }

  // 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");

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Check stream.pushAllowed before calling pushStream and degrade gracefully
  2. Wrap pushStream in try/catch and fall back to just responding without the pushed resource
  3. Prefer preload links or 103 Early Hints over HTTP/2 push, which is effectively deprecated across ecosystems

Example fix

// before
stream.pushStream(pushHeaders, (err, push) => { /* ... */ }); // throws when disabled

// after
if (stream.pushAllowed) {
  stream.pushStream(pushHeaders, (err, push) => { /* ... */ });
} else {
  stream.respond({ ':status': 200, link: '</app.js>; rel=preload' });
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (stream.pushAllowed) {
  stream.pushStream(pushHeaders, onPush);
} else {
  stream.respond({ ':status': 200, link: '</app.js>; rel=preload' });
}

Type guard

const canPush = (s: Http2Stream): boolean =>
  s.pushAllowed === true;

Try / catch

try {
  stream.pushStream(pushHeaders, onPush);
} catch (err) {
  if ((err as NodeJS.ErrnoException).code === 'ERR_HTTP2_PUSH_DISABLED') {
    // fall back to preload links / Early Hints in the normal response
  } else throw err;
}

Prevention

When it happens

Trigger: Client: http2.connect(url, { settings: { enablePush: false } }) and the server then calls pushStream; server pushing after the session received GOAWAY (pushAllowed goes false once closed/destroyed); pushing on a stream whose session already ended.

Common situations: Assuming server push works universally — browsers never supported or have disabled HTTP/2 push; client libraries that disable push by default to save bandwidth; racing a push against connection teardown.

Related errors


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