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
- Check stream.pushAllowed before calling pushStream and degrade gracefully
- Wrap pushStream in try/catch and fall back to just responding without the pushed resource
- 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
- Never assume push is available — browsers and many clients disable it
- Check pushAllowed before every pushStream
- Prefer 103 Early Hints or Link rel=preload as the portable alternative
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
- ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS
- ERR_HTTP2_INVALID_SETTING_VALUE
- ERR_HTTP2_NESTED_PUSH
- ERR_INVALID_URL
- ERR_HTTP2_NO_SOCKET_MANIPULATION
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d7bba57c9afd51d7.
Report an issue: GitHub.