denoland/deno · error · TypeError

Request closed

Error message

Request closed

What it means

InnerRequest.url() reads the request URL from the native handle; it caches the value on first access but the very first call must reach the handle. Once the request is closed (response delivered or connection finished) the handle is null and the URL is no longer queryable, so a TypeError 'Request closed' is thrown. It is a TypeError rather than Deno.errors.Http because url() is also used from non-upgrade paths.

Source

Thrown at ext/http/00_serve.ts:256

      this.url();
      this.headerList;
      this.remoteAddr;
      this.close();

      this.#upgraded = true;

      return op_http_upgrade_websocket_next(external);
    }
  }

  url() {
    if (this.#urlValue !== undefined) {
      return this.#urlValue;
    }

    if (this.#external === null) {
      throw new TypeError("Request closed");
    }

    if (this.#methodValue === undefined) {
      this.#methodValue = op_http_get_request_method(this.#external);
    }

    return this.#urlValue = op_http_get_request_url(this.#external);
  }

  get completed() {
    if (!this.#completed) {
      // NOTE: this is faster than Promise.withResolvers()
      let resolve, reject;
      const promise = new Promise((r1, r2) => {
        resolve = r1;
        reject = r2;
      });
      this.#completed = { promise, resolve, reject };

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Capture request.url (and anything else needed) into a local const at the top of the handler
  2. Pass plain values (method, url, headers) into background work instead of the Request object
  3. Wrap post-completion access in try/catch on TypeError when you cannot restructure

Example fix

// before
async function handler(req, info) {
  respond();
  setTimeout(() => log(req.url), 1000); // TypeError: Request closed
}

// after
async function handler(req, info) {
  const url = req.url; // capture eagerly
  setTimeout(() => log(url), 1000);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// capture eagerly at handler entry; no runtime check can resurrect a closed request
async function handler(req, info) {
  const meta = { url: req.url, method: req.method }; // safe while live
  // ... later use meta.url, never req.url post-completion
}

Try / catch

try {
  log(req.url);
} catch (err) {
  if (err instanceof TypeError && err.message === "Request closed") {
    log("<closed>");
  } else throw err;
}

Prevention

When it happens

Trigger: Accessing the request's URL lazily after the handler has returned and the response completed — e.g. in a promise callback, log flush, or error handler that captures the request object and touches url() post-completion.

Common situations: Deferred access logging that batches and reads request.url after the response is sent; background tasks capturing the request object; error reporting inside catch blocks that run after close; post-response analytics.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/7aa21f38afda9410. Report an issue: GitHub.