denoland/deno · error · TypeError
Cannot read url: request closed
Error message
Cannot read url: request closed
What it means
InnerRequest.currentUrl() lazily resolves the most recent URL in the request's URL list (used during redirect following) by calling the stored urlList callback. For op-backed requests that callback is a native read; if the request resource has been closed by the time currentUrl() runs, the native call throws and is re-thrown as TypeError "Cannot read url: request closed".
Solutions
- Forward a fresh Request built from snapshotted data instead of reusing the server request object
- Perform redirect-following fetches while the original request is still alive (start them in the handler)
- Capture req.url up front and construct a new Request(url, { method, headers, body }) for deferred work
Example fix
// before
Deno.serve((req) => {
jobs.push(() => fetch(req)); // later: TypeError: Cannot read url: request closed
return new Response("accepted");
});
// after
Deno.serve((req) => {
const snapshot = { url: req.url, method: req.method, headers: [...req.headers] };
jobs.push(() => fetch(snapshot.url, { method: snapshot.method, headers: snapshot.headers }));
return new Response("accepted");
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Capture before the handler returns, then re-fetch with explicit data:
function forwardLater(req) {
const snapshot = { url: req.url, method: req.method, headers: [...req.headers] };
return () => fetch(snapshot.url, { method: snapshot.method, headers: snapshot.headers });
} Try / catch
try {
await fetch(req); // may hit currentUrl() on redirects if the source closed
} catch (err) {
if (err instanceof TypeError && err.message.includes("request closed")) {
// rebuild from snapshot and retry once
} else throw err;
} Prevention
- Never pass an op-backed server Request into fetch() after the handler returned
- Start forwarding fetches synchronously inside the handler
- Build detached requests from snapshotted data for deferred/queued work
When it happens
Trigger: Redirect handling (fetch internals calling currentUrl on redirect) against an op-backed request that got closed mid-flight; user code or libraries that walk a server request's url list after the connection ended.
Common situations: Calling fetch() with an already-consumed server-side Request; proxies that forward the incoming request object asynchronously after responding; tests that reuse a request across ticks after the server released it.
Related errors
- cannot read url: request closed
- Cannot read headers: request closed
- Referrer " " is not a valid URL.
- Body is unusable
- Input request's body is unusable
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/c12435333278e771.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/23_request.js:177
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];
},
};
}
/**
* https://fetch.spec.whatwg.org/#concept-request-clone
* @param {InnerRequest} request
* @param {boolean} skipBody
* @returns {InnerRequest}
*/
function cloneInnerRequest(request, skipBody = false) {
const headerList = ArrayPrototypeMap(
request.headerList,
(x) => [x[0], x[1]],
);View on GitHub (pinned to 9ad36f7a2c)