denoland/deno · error · TypeError
Cannot read headers: request closed
Error message
Cannot read headers: request closed
What it means
InnerRequest's headerList getter materializes headers lazily by calling the headerList() function captured at request creation. When that underlying source (an op-backed request, e.g. one deserialized from a server connection or transfer stream) has already been closed, the call throws and the getter converts it into TypeError 'Cannot read headers: request closed'. It means the request object outlived the resource that owns its header data.
Source
Thrown at ext/fetch/23_request.js:146
StringPrototypeStartsWith(url, "blob:")
) {
blobUrlEntry = blobFromObjectUrl(url);
}
return {
methodInner: method,
get method() {
return this.methodInner;
},
set method(value) {
this.methodInner = value;
},
headerListInner: null,
get headerList() {
if (this.headerListInner === null) {
try {
this.headerListInner = headerList();
} catch {
throw new TypeError("Cannot read headers: request closed");
}
}
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]();View on GitHub (pinned to 89f33cbef2)
Solutions
- Copy out the headers (e.g. Object.fromEntries(request.headers)) synchronously when the request first arrives, before any await
- Stop touching the Request after the connection/transfer that owns it closes; pass plain data to deferred work instead
- Abort or finish in-flight request processing when the underlying connection's close/abort signal fires
Example fix
// before
const pending = [];
Deno.serve((req) => {
pending.push(req); // kept past connection lifetime
return new Response('ok');
});
// later, connection already closed:
for (const r of pending) console.log(r.headers.get('x-id')); // throws
// after
const pending = [];
Deno.serve((req) => {
pending.push(Object.fromEntries(req.headers)); // snapshot now
return new Response('ok');
}); Defensive patterns
Strategy: try-catch
Validate before calling
function snapshotHeaders(req) {
try {
return Object.fromEntries(req.headers);
} catch {
return null; // request already closed
}
} Try / catch
try {
headers = Object.fromEntries(req.headers);
} catch (err) {
if (err instanceof TypeError && err.message === 'Cannot read headers: request closed') {
// connection dropped; skip or use last-known snapshot
} else throw err;
} Prevention
- Materialize headers synchronously when a request arrives, before any await
- Never retain Request objects past response completion; keep plain snapshots
- Watch request.signal.aborted and stop processing retained requests
When it happens
Trigger: Reading request.headers (directly or via fetch internals) on an InnerRequest whose op resource was closed beforehand — e.g. a server-side or worker-transferred Request accessed after the connection/MessagePort that produced it closed.
Common situations: Stashing an incoming Request for later processing after the HTTP connection dropped, or after response completion when the runtime reaped the connection resource; race between an aborted client and handler code still inspecting headers.
Related errors
- cannot read url: 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/8318bcaccafdffbd.
Report an issue: GitHub.