{"record":{"id":"0de173d566857279","repo":"denoland/deno","slug":"cannot-read-url-request-closed","errorCode":null,"errorMessage":"cannot read url: request closed","messagePattern":"cannot read url: request closed","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/23_request.js","lineNumber":166,"sourceCode":"      }\n      return this.headerListInner;\n    },\n    set headerList(value) {\n      this.headerListInner = value;\n    },\n    body,\n    redirectMode: \"follow\",\n    redirectCount: 0,\n    urlList: [typeof url === \"string\" ? () => url : url],\n    urlListProcessed: [],\n    clientRid: null,\n    blobUrlEntry,\n    url() {\n      if (this.urlListProcessed[0] === undefined) {\n        try {\n          this.urlListProcessed[0] = this.urlList[0]();\n        } catch {\n          throw new TypeError(\"cannot read url: request closed\");\n        }\n      }\n      return this.urlListProcessed[0];\n    },\n    currentUrl() {\n      const currentIndex = this.urlList.length - 1;\n      if (this.urlListProcessed[currentIndex] === undefined) {\n        try {\n          this.urlListProcessed[currentIndex] = this.urlList[currentIndex]();\n        } catch {\n          throw new TypeError(\"Cannot read url: request closed\");\n        }\n      }\n      return this.urlListProcessed[currentIndex];\n    },\n  };\n}\n","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/23_request.js#L148-L184","documentation":"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.","triggerScenarios":"Calling request.url on a lazily-resolved request whose underlying resource (server connection / transfer channel) closed before the first url() access.","commonSituations":"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.","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"],"exampleFix":"// before\nasync function handleLater(req) { /* ... */ }\nDeno.serve((req) => {\n  schedule(() => log(req.url)); // req's resource may be closed by then\n  return new Response('ok');\n});\n\n// after\nDeno.serve((req) => {\n  const url = req.url; // materialize now\n  schedule(() => log(url));\n  return new Response('ok');\n});","handlingStrategy":"validation","validationCode":"function captureUrl(req) {\n  try {\n    return req.url; // force lazy resolution while resource is open\n  } catch {\n    return null;\n  }\n}\nconst url = captureUrl(req);","typeGuard":null,"tryCatchPattern":"try {\n  url = req.url;\n} catch (err) {\n  if (err instanceof TypeError && err.message === 'cannot read url: request closed') {\n    url = fallbackUrl; // e.g. from a header you already snapshotted\n  } else throw err;\n}","preventionTips":["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"],"tags":["fetch","request","lifecycle","race-condition"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}