denoland/deno · warning · WebTransportError

WebTransport is not connected

Error message

WebTransport is not connected

What it means

WebTransport.close() requires an established session; if close() is called before the connection op completed, the internal connection handle is still unset and Deno throws a WebTransportError (source: "session") from ext/web/webtransport.js. This typically happens in cleanup code that runs immediately after constructing the transport or right after a failed connection.

Source

Thrown at ext/web/webtransport.js:332

  get anticipatedConcurrentIncomingBidirectionalStreams() {
    webidl.assertBranded(this, WebTransportPrototype);
    return this.#anticipatedConcurrentIncomingBidirectionalStreams;
  }

  get closed() {
    webidl.assertBranded(this, WebTransportPrototype);
    return this.#closed.promise;
  }

  close(closeInfo) {
    webidl.assertBranded(this, WebTransportPrototype);
    closeInfo = webidl.converters.WebTransportCloseInfo(
      closeInfo,
      "Failed to execute 'close' on 'WebTransport'",
      "Argument 1",
    );
    if (!this.#conn) {
      throw new WebTransportError("WebTransport is not connected", {
        source: "session",
      });
    }
    this.#conn.close({
      closeCode: closeInfo.closeCode,
      reason: closeInfo.reason,
    });
  }

  get datagrams() {
    webidl.assertBranded(this, WebTransportPrototype);
    return this.#datagrams;
  }

  async createBidirectionalStream(options) {
    webidl.assertBranded(this, WebTransportPrototype);
    options = webidl.converters.WebTransportSendStreamOptions(
      options,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Await `wt.ready` before closing, or track connection state yourself.
  2. Wrap close() in try/catch and ignore WebTransportError when the session never connected.
  3. In cleanup paths, use `wt.closed.catch(() => {})` to observe termination without throwing.
  4. Cancel pending work via your own AbortController rather than closing an unconnected session.

Example fix

// before
const wt = new WebTransport(url);
doWork().finally(() => wt.close()); // may throw before connect

// after
const wt = new WebTransport(url);
try {
  await wt.ready;
  await doWork();
} finally {
  try { wt.close({}); } catch { /* not connected */ }
}
Defensive patterns

Strategy: try-catch

Validate before calling

let connected = false;
const wt = new WebTransport(url);
await wt.ready.then(() => { connected = true; });
function closeIfConnected() {
  if (connected) wt.close({});
}

Type guard

function isNotConnectedError(e) {
  return e.name === "WebTransportError" &&
    e.message === "WebTransport is not connected";
}

Try / catch

try {
  wt.close({ closeCode: 0, reason: "done" });
} catch (e) {
  if (!(e.name === "WebTransportError" && e.message === "WebTransport is not connected")) throw e;
}

Prevention

When it happens

Trigger: const wt = new WebTransport(url); wt.close(); on adjacent lines; finally-block cleanup racing the not-yet-settled connection; error handling that closes the session before awaiting ready; close() after the connection already failed.

Common situations: try/finally wrappers that always close; timeouts firing before connection completes; UI unmount handlers closing a transport that never connected; aborting a page/component during connection.

Related errors


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