denoland/deno · error · TypeError

cannot read url: request closed

Error message

cannot read url: request closed

What it means

InnerRequest.url() computes the URL lazily by invoking the closure stored in urlList[0]. For op-backed requests that closure reads from a live resource; once that resource is closed it throws, and the getter rewrites the failure as TypeError 'cannot read url: request closed' (lowercase 'cannot', matching the url() accessor). The URL was never cached in urlListProcessed, so it can no longer be obtained.

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 89f33cbef2)

Solutions

  1. Read request.url immediately when the request arrives, before any await, and store the string
  2. Check request.signal.aborted (or the connection state) before touching a retained request
  3. Pass { url, method, headers } snapshots into deferred jobs instead of the Request object

Example fix

// before
async function handleLater(req) { /* ... */ }
Deno.serve((req) => {
  schedule(() => log(req.url)); // req's resource may be closed by then
  return new Response('ok');
});

// after
Deno.serve((req) => {
  const url = req.url; // materialize now
  schedule(() => log(url));
  return new Response('ok');
});
Defensive patterns

Strategy: validation

Validate before calling

function captureUrl(req) {
  try {
    return req.url; // force lazy resolution while resource is open
  } catch {
    return null;
  }
}
const url = captureUrl(req);

Try / catch

try {
  url = req.url;
} catch (err) {
  if (err instanceof TypeError && err.message === 'cannot read url: request closed') {
    url = fallbackUrl; // e.g. from a header you already snapshotted
  } else throw err;
}

Prevention

When it happens

Trigger: Calling request.url on a lazily-resolved request whose underlying resource (server connection / transfer channel) closed before the first url() access.

Common situations: Queueing incoming Requests and processing them asynchronously after the client disconnected, or inspecting a transferred request in a worker after the sender closed the channel.

Related errors


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