denoland/deno · error · TypeError

cannot read url: request closed

Error message

cannot read url: request closed

What it means

An op-backed InnerRequest (typically a server-side Request from Deno.serve) resolves its url lazily: the url() getter calls the stored urlList callback, which performs a native read of the request's URL the first time .url is accessed. If the underlying request resource is already closed, that native call throws and is re-thrown as TypeError "cannot read url: request closed" (note the lowercase 'c' in this variant).

Solutions

  1. Capture const url = req.url synchronously as the first statement of the handler
  2. Pass the captured string (not the request object) into async/background code
  3. If you must defer the whole request, snapshot url, method, and headers up front

Example fix

// before
Deno.serve(async (req) => {
  await doSlowWork();
  log(req.url); // TypeError: cannot read url: request closed
  return new Response("ok");
});

// after
Deno.serve(async (req) => {
  const url = req.url; // capture synchronously
  await doSlowWork();
  log(url);
  return new Response("ok");
});
Defensive patterns

Strategy: try-catch

Validate before calling

// No pre-check API exists — capture the string synchronously:
function handle(req) {
  const url = req.url; // materialize now; pass `url` (not `req`) onward
  queueMicrotask(() => log(url));
}

Try / catch

try {
  const url = req.url;
} catch (err) {
  if (err instanceof TypeError && err.message.includes("request closed")) {
    // request resource released; fall back to captured metadata
  } else throw err;
}

Prevention

When it happens

Trigger: Reading req.url on a server Request after the handler returned or after the connection/request resource was closed — e.g. inside setTimeout, a detached promise, or a background worker that received the raw request object.

Common situations: Async logging or metrics that reference req.url after the response is sent; request objects pushed to an in-memory job queue; event handlers attached to a server that fire after the request lifecycle ends.

Related errors


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

Appendix: source

Thrown at ext/fetch/23_request.js:166

      }
      return this.headerListInner;
    },
    set headerList(value) {
      this.headerListInner = value;
    },
    body,
    redirectMode: "follow",
    redirectCount: 0,
    urlList: [typeof url === "string" ? () => url : url],
    urlListProcessed: [],
    clientRid: null,
    blobUrlEntry,
    url() {
      if (this.urlListProcessed[0] === undefined) {
        try {
          this.urlListProcessed[0] = this.urlList[0]();
        } catch {
          throw new TypeError("cannot read url: request closed");
        }
      }
      return this.urlListProcessed[0];
    },
    currentUrl() {
      const currentIndex = this.urlList.length - 1;
      if (this.urlListProcessed[currentIndex] === undefined) {
        try {
          this.urlListProcessed[currentIndex] = this.urlList[currentIndex]();
        } catch {
          throw new TypeError("Cannot read url: request closed");
        }
      }
      return this.urlListProcessed[currentIndex];
    },
  };
}

View on GitHub (pinned to 9ad36f7a2c)