{"record":{"id":"7aa21f38afda9410","repo":"denoland/deno","slug":"request-closed","errorCode":null,"errorMessage":"Request closed","messagePattern":"Request closed","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/00_serve.ts","lineNumber":271,"sourceCode":"\n      this.url();\n      this.headerList;\n      this.remoteAddr;\n      this.close();\n\n      this.#upgraded = true;\n\n      return op_http_upgrade_websocket_next(external);\n    }\n  }\n\n  url() {\n    if (this.#urlValue !== undefined) {\n      return this.#urlValue;\n    }\n\n    if (this.#external === null) {\n      throw new TypeError(\"Request closed\");\n    }\n\n    if (this.#methodValue === undefined) {\n      this.#methodValue = op_http_get_request_method(this.#external);\n    }\n\n    return this.#urlValue = op_http_get_request_url(this.#external);\n  }\n\n  get completed() {\n    if (!this.#completed) {\n      // NOTE: this is faster than Promise.withResolvers()\n      let resolve, reject;\n      const promise = new Promise((r1, r2) => {\n        resolve = r1;\n        reject = r2;\n      });\n      this.#completed = { promise, resolve, reject };","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/denoland/deno/blob/abd22074e47c6a5cd14e9e4e84743f084aa5a575/ext/http/00_serve.ts#L253-L289","documentation":"The internal serve request object caches its URL lazily from the native handle. Once the request is finalized and the native handle released (#external === null), calling url() throws a TypeError ('Request closed'). Practically: reading the request URL after the response has completed touches a dead resource.","triggerScenarios":"Accessing the request's URL in a detached task (fire-and-forget promise) after the handler returned; logging or metrics code that reads request.url after await respondWith(...); accessing a stored request object in a queue processed after the response finished.","commonSituations":"Access logs written in background flushers; request objects captured in closures and inspected later; spans/traces closed asynchronously after the response.","solutions":["Read request.url (and any other request data) synchronously at handler entry and capture it into a local","Pass plain copied values (url string, method, headers snapshot) to async/background work instead of the Request object","Move post-response logging into code that already holds the captured values, not the request"],"exampleFix":"// before\nDeno.serve((req) => {\n  queueMicrotask(() => log(req.url)); // may run after close -> TypeError\n  return new Response(\"ok\");\n});\n\n// after\nDeno.serve((req) => {\n  const url = req.url; // capture synchronously\n  queueMicrotask(() => log(url));\n  return new Response(\"ok\");\n});","handlingStrategy":"validation","validationCode":"Deno.serve((req) => {\n  const ctx = {\n    url: req.url,               // capture synchronously\n    method: req.method,\n    headers: [...req.headers],  // snapshot\n  };\n  scheduleBackgroundLog(ctx);   // safe: only uses the snapshot\n  return handle(ctx, req);\n});","typeGuard":null,"tryCatchPattern":"try {\n  log(req.url);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"Request closed\") {\n    // too late: fall back to a previously captured value\n  } else throw err;\n}","preventionTips":["Capture request.url/method/headers into locals at handler entry","Pass plain copied data to background tasks, never the Request object","Finish request-dependent logging before returning the response"],"tags":["http","serve","request","lifecycle"],"backgroundTag":"resource-use-after-close","analyzedSha":"abd22074e47c6a5cd14e9e4e84743f084aa5a575","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}