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
- Await `wt.ready` before closing, or track connection state yourself.
- Wrap close() in try/catch and ignore WebTransportError when the session never connected.
- In cleanup paths, use `wt.closed.catch(() => {})` to observe termination without throwing.
- 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
- Await ready before registering cleanup handlers that call close().
- Make close() idempotent in your wrapper and swallow the not-connected error.
- Watch wt.closed to detect termination instead of polling close().
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
- SyntaxError
- Destroy hook of "${id}" errored
- Invalid header: length must be 2, but is ${header.length}
- Invalid header name: "${name}"
- Cannot change header: headers are immutable
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/9df3de59d9f9ad9b.
Report an issue: GitHub.