websockets/ws · critical · Error
server.handleUpgrade() was called more than once with the sa
Error message
server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration
What it means
Thrown by WebSocketServer.completeUpgrade() (lib/websocket-server.js:378-383) when the same network socket has already been upgraded and tagged via socket[kWebSocket]. This happens when handleUpgrade() is invoked more than once for a single TCP socket, which the library treats as a misconfiguration because a socket can only back one WebSocket connection. It is a synchronous throw from within handleUpgrade() and will crash the process if uncaught.
Source
Thrown at lib/websocket-server.js:379
*
* @param {Object} extensions The accepted extensions
* @param {String} key The value of the `Sec-WebSocket-Key` header
* @param {Set} protocols The subprotocols
* @param {http.IncomingMessage} req The request object
* @param {Duplex} socket The network socket between the server and client
* @param {Buffer} head The first packet of the upgraded stream
* @param {Function} cb Callback
* @throws {Error} If called more than once with the same socket
* @private
*/
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
//
// Destroy the socket if the client has already sent a FIN packet.
//
if (!socket.readable || !socket.writable) return socket.destroy();
if (socket[kWebSocket]) {
throw new Error(
'server.handleUpgrade() was called more than once with the same ' +
'socket, possibly due to a misconfiguration'
);
}
if (this._state > RUNNING) return abortHandshake(socket, 503);
const digest = createHash('sha1')
.update(key + GUID)
.digest('base64');
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${digest}`
];
View on GitHub (pinned to ae1de54330)
Solutions
- Ensure each 'upgrade' event is handled by exactly one WebSocketServer: use the path option or a custom shouldHandle/verifyClient to route.
- If you need two servers on one HTTP server, branch in your single 'upgrade' listener and call handleUpgrade on only one of them.
- Avoid registering multiple 'upgrade' listeners that all call handleUpgrade unconditionally.
Example fix
// before
httpServer.on('upgrade', (req, socket, head) => {
wss1.handleUpgrade(req, socket, head, () => {});
wss2.handleUpgrade(req, socket, head, () => {}); // double upgrade!
});
// after
httpServer.on('upgrade', (req, socket, head) => {
const { pathname } = new URL(req.url, 'http://x');
if (pathname === '/a') wss1.handleUpgrade(req, socket, head, () => {});
else wss2.handleUpgrade(req, socket, head, () => {});
}); Defensive patterns
Strategy: validation
Validate before calling
httpServer.on('upgrade', (req, socket, head) => {
if (socket[kWebSocket]) return; // already upgraded, ignore
const { pathname } = new URL(req.url, 'http://x');
const target = pathname === '/a' ? wss1 : wss2;
target.handleUpgrade(req, socket, head, (ws) => {/* ... */});
}); Type guard
const { kWebSocket } = require('ws/lib/constants');
function isSocketUpgradeable(socket) {
return !socket[kWebSocket];
} Try / catch
try {
wss.handleUpgrade(req, socket, head, cb);
} catch (err) {
if (/more than once with the same socket/.test(err.message)) {
// double upgrade: ensure only one WSS handles this socket
socket.destroy();
} else {
throw err;
}
} Prevention
- Route each 'upgrade' event to exactly one WebSocketServer via path or shouldHandle.
- Never register multiple 'upgrade' listeners that all call handleUpgrade unconditionally.
- If using a single HTTP server with two endpoints, branch in one listener and pick the target.
When it happens
Trigger: Attaching two or more WebSocketServer instances to the same HTTP server's 'upgrade' event and both calling handleUpgrade on the same req/socket; calling wss.handleUpgrade() manually in a loop or in overlapping branches of an if/else; a single server with both { server } mode and a manual 'upgrade' handler that both forward to handleUpgrade.
Common situations: Running multiple WebSocket endpoints on one HTTP server without using the path option or shouldHandle to discriminate; copy-pasting the noServer example twice; middleware that re-emits 'upgrade' events; upgrading from one WSS to two without splitting upgrade handling.
Related errors
- One and only one of the "port", "server", or "noServer" opti
- The server is operating in "noServer" mode
- Unsupported protocol version: ${opts.protocolVersion} (suppo
AI-assisted analysis of websockets/ws@ae1de54330 (2026-08-03).
Data as JSON: /data/errors/7a13157ac0069a3d.json.
Report an issue: GitHub.