{"record":{"id":"d7bba57c9afd51d7","repo":"denoland/deno","slug":"err-http2-push-disabled","errorCode":"ERR_HTTP2_PUSH_DISABLED","errorMessage":"HTTP/2 client has disabled push streams","messagePattern":"HTTP/2 client has disabled push streams","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":3087,"sourceCode":"    this[kInit](id, handle);\n    this[kProtocol] = headers[HTTP2_HEADER_SCHEME];\n    this[kAuthority] = getAuthority(headers);\n  }\n\n  // True if the remote peer accepts push streams\n  get pushAllowed() {\n    return !this.destroyed &&\n      !this.closed &&\n      !this.session.closed &&\n      !this.session.destroyed &&\n      this[kSession].remoteSettings.enablePush;\n  }\n\n  // Create a push stream, call the given callback with the created\n  // Http2Stream for the push stream.\n  pushStream(headers, options, callback) {\n    if (!this.pushAllowed) {\n      throw new ERR_HTTP2_PUSH_DISABLED();\n    }\n    if (this[kID] % 2 === 0) {\n      throw new ERR_HTTP2_NESTED_PUSH();\n    }\n\n    const session = this[kSession];\n\n    debugStreamObj(this, \"initiating push stream\");\n\n    this[kUpdateTimer]();\n\n    if (typeof options === \"function\") {\n      callback = options;\n      options = undefined;\n    }\n\n    validateFunction(callback, \"callback\");\n","sourceCodeStart":3069,"sourceCodeEnd":3105,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L3069-L3105","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nstream.pushStream(pushHeaders, (err, push) => { /* ... */ }); // throws when disabled\n\n// after\nif (stream.pushAllowed) {\n  stream.pushStream(pushHeaders, (err, push) => { /* ... */ });\n} else {\n  stream.respond({ ':status': 200, link: '</app.js>; rel=preload' });\n}","handlingStrategy":"try-catch","validationCode":"if (stream.pushAllowed) {\n  stream.pushStream(pushHeaders, onPush);\n} else {\n  stream.respond({ ':status': 200, link: '</app.js>; rel=preload' });\n}","typeGuard":"const canPush = (s: Http2Stream): boolean =>\n  s.pushAllowed === true;","tryCatchPattern":"try {\n  stream.pushStream(pushHeaders, onPush);\n} catch (err) {\n  if ((err as NodeJS.ErrnoException).code === 'ERR_HTTP2_PUSH_DISABLED') {\n    // fall back to preload links / Early Hints in the normal response\n  } else throw err;\n}","preventionTips":["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"],"tags":["http2","server-push","settings","node-compat"],"backgroundTag":"http2-push-disabled","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}