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
- Capture const url = req.url synchronously as the first statement of the handler
- Pass the captured string (not the request object) into async/background code
- 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
- Make 'capture url/method/headers first' a convention in every handler
- Pass primitives (strings, arrays) into background tasks, never Request objects
- In tests, keep the request alive or assert against the snapshot
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
- Cannot read headers: request closed
- Cannot read url: 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/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)