{"record":{"id":"7a13157ac0069a3d","repo":"websockets/ws","slug":"server-handleupgrade-was-called-more-than-once-w","errorCode":null,"errorMessage":"server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration","messagePattern":"server\\.handleUpgrade\\(\\) was called more than once with the same socket, possibly due to a misconfiguration","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"lib/websocket-server.js","lineNumber":379,"sourceCode":"   *\n   * @param {Object} extensions The accepted extensions\n   * @param {String} key The value of the `Sec-WebSocket-Key` header\n   * @param {Set} protocols The subprotocols\n   * @param {http.IncomingMessage} req The request object\n   * @param {Duplex} socket The network socket between the server and client\n   * @param {Buffer} head The first packet of the upgraded stream\n   * @param {Function} cb Callback\n   * @throws {Error} If called more than once with the same socket\n   * @private\n   */\n  completeUpgrade(extensions, key, protocols, req, socket, head, cb) {\n    //\n    // Destroy the socket if the client has already sent a FIN packet.\n    //\n    if (!socket.readable || !socket.writable) return socket.destroy();\n\n    if (socket[kWebSocket]) {\n      throw new Error(\n        'server.handleUpgrade() was called more than once with the same ' +\n          'socket, possibly due to a misconfiguration'\n      );\n    }\n\n    if (this._state > RUNNING) return abortHandshake(socket, 503);\n\n    const digest = createHash('sha1')\n      .update(key + GUID)\n      .digest('base64');\n\n    const headers = [\n      'HTTP/1.1 101 Switching Protocols',\n      'Upgrade: websocket',\n      'Connection: Upgrade',\n      `Sec-WebSocket-Accept: ${digest}`\n    ];\n","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/websockets/ws/blob/c791e707eab3c13dd9a261d2479c3cc4a49a6fed/lib/websocket-server.js#L361-L397","documentation":"Thrown by WebSocketServer.completeUpgrade() at websocket-server.js:378-383 when the same network socket already has a WebSocket associated with it (socket[kWebSocket] is set). This symbol is assigned during setSocket() at websocket.js:233, so seeing it means completeUpgrade already ran for this socket. The library treats a second upgrade on one socket as a configuration error, not a normal event.","triggerScenarios":"Two WebSocketServer instances both attach their 'upgrade' listener to the same httpServer and both call handleUpgrade for the same request; a user manually calls wss.handleUpgrade() in their own 'upgrade' listener while the server also has its auto-attached listener; an 'upgrade' event fires twice on the same socket due to a proxy or framework quirk.","commonSituations":"Mounting multiple WebSocketServer instances on one HTTP server without a path filter (options.path) or without noServer routing; a framework (e.g. Express/Next/Fastify adapter) that adds its own upgrade handler that double-dispatches; accidentally calling handleUpgrade inside both verifyClient's callback and the default path.","solutions":["Ensure only ONE WebSocketServer (or one manual handleUpgrade call) processes each upgrade request. Use options.path to route by URL, or use noServer mode with explicit routing in your own 'upgrade' handler.","If you need multiple WS endpoints on one HTTP server, use noServer: true and dispatch based on req.url in a single 'upgrade' listener: if (req.url === '/a') wssA.handleUpgrade(...); else wssB.handleUpgrade(...).","Remove any duplicate 'upgrade' listener you added manually if the server already auto-attaches one (server/noServer modes do this in the constructor at websocket-server.js:125-131)."],"exampleFix":"// before — two servers on one HTTP server both grab every upgrade\nconst wssA = new WebSocketServer({ server: httpServer, path: '/a' });\nconst wssB = new WebSocketServer({ server: httpServer, path: '/b' });\n// (path helps, but safest is noServer routing)\n\n// after — single noServer instance with manual routing\nconst wssA = new WebSocketServer({ noServer: true });\nconst wssB = new WebSocketServer({ noServer: true });\nhttpServer.on('upgrade', (req, socket, head) => {\n  const { pathname } = new URL(req.url, 'http://x');\n  if (pathname === '/a') wssA.handleUpgrade(req, socket, head, cb);\n  else if (pathname === '/b') wssB.handleUpgrade(req, socket, head, cb);\n  else socket.destroy();\n});","handlingStrategy":"validation","validationCode":"// Use noServer mode with explicit routing to avoid double-dispatch\nfunction attachWebSocketServers(httpServer, servers) {\n  httpServer.on('upgrade', (req, socket, head) => {\n    const { pathname } = new URL(req.url, 'http://placeholder');\n    const wss = servers.find(s => s.options.path === pathname);\n    if (wss) {\n      wss.handleUpgrade(req, socket, head, (ws) => { /* ... */ });\n    } else {\n      socket.destroy();\n    }\n  });\n}","typeGuard":"function isSocketAlreadyUpgraded(socket) {\n  return socket[kWebSocket] !== undefined;\n}","tryCatchPattern":"try {\n  wss.handleUpgrade(req, socket, head, cb);\n} catch (err) {\n  if (/more than once with the same socket/.test(err.message)) {\n    // a prior handler already upgraded this socket; just destroy to be safe\n    socket.destroy();\n  } else throw err;\n}","preventionTips":["Use noServer mode and a single 'upgrade' listener with explicit URL-based routing when you need multiple endpoints on one HTTP server.","Never attach two WebSocketServer instances to the same httpServer without disjoint path filters.","Don't call handleUpgrade manually if the server already auto-attaches its own upgrade listener (port/server modes)."],"tags":["websocket-server","handleupgrade","configuration","multiple-servers","upgrade"],"backgroundTag":null,"analyzedSha":"c791e707eab3c13dd9a261d2479c3cc4a49a6fed","analyzedAt":"2026-08-06T19:07:51.047Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}