{"record":{"id":"e2d57c933da2542a","repo":"denoland/deno","slug":"socket-is-already-destroyed-cannot-upgrade-to-we","errorCode":null,"errorMessage":"Socket is already destroyed - cannot upgrade to WebSocket","messagePattern":"Socket is already destroyed - cannot upgrade to WebSocket","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/02_websocket.ts","lineNumber":156,"sourceCode":"      }\n    })();\n  } else if (options.socket) {\n    // node:http upgrade path: the socket is a node net.Socket from the\n    // \"upgrade\" event. Write the 101 response, take the TCP stream from\n    // libuv, and create a WebSocket over it via ext/http.\n    const nodeSocket = options.socket;\n\n    // Build the 101 response from r.headerList so the headers stay in\n    // sync with the header-list built above (protocol negotiation, etc.).\n    let responseHead = \"HTTP/1.1 101 Switching Protocols\\r\\n\";\n    for (let i = 0; i < r.headerList.length; i++) {\n      const { 0: name, 1: value } = r.headerList[i];\n      responseHead += `${name}: ${value}\\r\\n`;\n    }\n    responseHead += \"\\r\\n\";\n\n    if (nodeSocket.destroyed) {\n      throw new TypeError(\n        \"Socket is already destroyed - cannot upgrade to WebSocket\",\n      );\n    }\n    const handle = nodeSocket._handle;\n    if (!handle) {\n      throw new TypeError(\"Socket has no handle - cannot upgrade\");\n    }\n    if (typeof handle.takeStream !== \"function\") {\n      throw new TypeError(\n        \"Socket is not a TCP socket - only TCP connections can be upgraded to WebSocket\",\n      );\n    }\n\n    // Extra bytes that were already buffered (e.g., from the upgrade\n    // request body that arrived with the headers)\n    const extraBytes = options.head || new Uint8Array(0);\n\n    // Defer setup so the caller can attach event handlers (onopen,","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/http/02_websocket.ts#L138-L174","documentation":"On the node:http upgrade path of Deno.upgradeWebSocket (when options.socket is a node net.Socket received from a server 'upgrade' event), Deno must write the 101 response and detach the TCP stream from libuv. It refuses if nodeSocket.destroyed is already true, because a destroyed socket cannot carry the WebSocket traffic.","triggerScenarios":"Calling Deno.upgradeWebSocket(req, { socket }) inside a server.on('upgrade') handler after the socket timed out, errored, or was explicitly destroyed (e.g. in a prior error branch or by middleware that closes sockets); deferring the upgrade asynchronously until after disconnect; client disconnecting between the upgrade event and the handler body.","commonSituations":"Express/Node-compat servers where earlier middleware or error handlers call socket.destroy()/res.socket.destroy(); idle-timeout policies (server.headersTimeout/requestTimeout) destroying sockets; race with client cancel during slow async handlers; double handling of the same upgrade event by two listeners.","solutions":["Check socket.destroyed before upgrading and bail out (close/ignore) instead of throwing.","Perform the upgrade synchronously in the 'upgrade' event handler, not after awaiting something.","Remove middleware/error paths that destroy upgrade sockets before the handler runs.","Raise or disable the relevant socket timeouts if long setup is required."],"exampleFix":"// before\nserver.on(\"upgrade\", (req, socket) => {\n  socket.destroy(); // e.g. auth failure path ran first\n  const { response } = Deno.upgradeWebSocket(req, { socket, head });\n});\n\n// after\nserver.on(\"upgrade\", async (req, socket, head) => {\n  if (socket.destroyed) return; // guard\n  const { socket: ws, response } = Deno.upgradeWebSocket(req, { socket, head });\n  ws.on(\"message\", (m) => console.log(m));\n});","handlingStrategy":"validation","validationCode":"server.on(\"upgrade\", (req, socket, head) => {\n  if (socket.destroyed) return; // client gone / earlier destroy\n  const { socket: ws } = Deno.upgradeWebSocket(req, { socket, head });\n});","typeGuard":"function isLiveNodeSocket(s: unknown): boolean { const sock = s as { destroyed?: boolean; _handle?: unknown } | null; return !!sock && sock.destroyed !== true; }","tryCatchPattern":"try { Deno.upgradeWebSocket(req, { socket, head }); } catch (e) { if (e instanceof TypeError && e.message.includes(\"already destroyed\")) { socket.destroy(); return; } throw e; }","preventionTips":["Upgrade synchronously inside the 'upgrade' event, before any await.","Audit error/middleware paths so nothing destroys upgrade sockets first.","Attach a 'close'/'error' listener on the socket to short-circuit late upgrades."],"tags":["websocket","node-compat","net-socket","upgrade"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}