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
- Read request.url immediately when the request arrives, before any await, and store the string
- Check request.signal.aborted (or the connection state) before touching a retained request
- 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
- Read req.url immediately upon receipt and pass the string onward
- For background jobs, enqueue { url, method, headers } records instead of Request objects
- Abort deferred request work when the connection's signal fires
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
- Cannot read headers: request closed
- Cannot read url: request closed
- Invalid header: length must be 2, but is ${header.length}
- Method is not valid
- Method is forbidden
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/0de173d566857279.
Report an issue: GitHub.