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
- Only initiate pushStream from the original request stream (odd ID); never from inside a push callback
- Pass the original stream (or its session + explicit flag) through your helper so pushes always root at the request
- 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
- Pass the original request stream through helpers; never reuse the stream a push callback gives you as a push initiator
- Flatten dependency pushes into one batch from the request stream
- Guard push helpers with an odd-ID check so push streams can never recurse
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
- ERR_HTTP2_TRAILERS_ALREADY_SENT
- ERR_HTTP2_TRAILERS_NOT_READY
- ERR_HTTP2_PUSH_DISABLED
- ERR_INVALID_URL
- ERR_HTTP2_NO_SOCKET_MANIPULATION
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/18782dc2dbfb0d59.
Report an issue: GitHub.