denoland/deno · error · Error

ERR_HTTP2_INVALID_SESSION

ERR_HTTP2_INVALID_SESSION

Error message

The session has been destroyed

What it means

Thrown by Http2Session.setNextStreamID() when the session's destroyed flag is already true. Like Node's http2 core, this polyfill refuses any method that mutates protocol state once the session is torn down, because the underlying nghttp2 handle no longer exists. setNextStreamID sets the ID of the next stream this session will create, which only has meaning on a live session.

Source

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

  }

  // Resets the timeout counter
  [kUpdateTimer]() {
    if (this.destroyed) {
      return;
    }
    if (this[kTimeout]) {
      this[kTimeout].refresh();
      syncSessionTimeoutInspectLinks(this[kTimeout]);
    }
  }

  // Sets the id of the next stream to be created by this Http2Session.
  // The value must be a number in the range 0 <= n <= kMaxStreams. The
  // value also needs to be larger than the current next stream ID.
  setNextStreamID(id) {
    if (this.destroyed) {
      throw new ERR_HTTP2_INVALID_SESSION();
    }

    validateNumber(id, "id");
    if (id <= 0 || id > kMaxStreams) {
      throw new ERR_OUT_OF_RANGE("id", `> 0 and <= ${kMaxStreams}`, id);
    }
    this[kHandle].setNextStreamID(id);
  }

  // Sets the local window size (local endpoints's window size)
  // Returns 0 if success or throw an exception if NGHTTP2_ERR_NOMEM
  // if the window allocation fails
  setLocalWindowSize(windowSize) {
    if (this.destroyed) {
      throw new ERR_HTTP2_INVALID_SESSION();
    }

    validateInt32(windowSize, "windowSize", 0);

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Check session.destroyed (and session.closed) before calling setNextStreamID, and reconnect first if either is true
  2. Recreate the session with http2.connect(origin) and call setNextStreamID on the fresh session before the first request()
  3. Move the call into the session's 'connect' event handler so it always runs on a healthy session

Example fix

// before
session.setNextStreamID(nextId); // throws ERR_HTTP2_INVALID_SESSION if destroyed

// after
if (session.destroyed || session.closed) {
  session = http2.connect(origin);
}
session.setNextStreamID(nextId);
Defensive patterns

Strategy: validation

Validate before calling

if (session.destroyed || session.closed) {
  session = http2.connect(origin);
}
session.setNextStreamID(nextId);

Type guard

function isSessionUsable(s) {
  return !s.destroyed && !s.closed;
}

Try / catch

try {
  session.setNextStreamID(id);
} catch (e) {
  if (e.code === 'ERR_HTTP2_INVALID_SESSION') { session = http2.connect(origin); session.setNextStreamID(id); }
  else throw e;
}

Prevention

When it happens

Trigger: Calling http2session.setNextStreamID(id) after session.destroy(), after a socket 'error'/'close' event auto-destroyed the session, or from a callback/timer that fires after teardown finished.

Common situations: Reusing a cached or pooled ClientHttp2Session after the server closed the connection; test code that destroys the session in an afterEach hook while a pending callback still calls setNextStreamID; long-lived processes that keep one session across reconnects without checking liveness.

Related errors


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