websockets/ws · warning · SyntaxError

The " " subprotocol is duplicated

Error message

The "${protocol}" subprotocol is duplicated

What it means

Thrown by subprotocol.parse() at subprotocol.js:37-38 when a protocol token delimited by commas has already been seen earlier in the same Sec-WebSocket-Protocol header. RFC 6455 §4.1 requires the client request at most one occurrence of each value; the server parser rejects duplicates using a Set.

Solutions

  1. Dedupe the protocol list on the client before connecting: [...new Set(protocols)].join(',').
  2. If using the ws client constructor, pass a de-duplicated array: new WebSocket(url, [...new Set(protocols)]).
  3. On the server, no action needed — handleUpgrade already catches and rejects with HTTP 400.

Example fix

// before
const ws = new WebSocket(url, ['chat', 'chat', 'json']);

// after
const ws = new WebSocket(url, [...new Set(['chat', 'chat', 'json'])]);
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: dedupe before sending
function uniqueProtocols(protocols) {
  return [...new Set(protocols.filter(p => typeof p === 'string' && p.length > 0))];
}
// usage: new WebSocket(url, uniqueProtocols(list));

Type guard

function hasNoDuplicateProtocols(protocols) {
  return new Set(protocols).size === protocols.length;
}

Try / catch

const { parse } = require('ws/lib/subprotocol');
try {
  protocols = parse(header);
} catch (err) {
  if (/duplicated/.test(err.message)) {
    // client sent a duplicate protocol; reject
  }
  socket.destroy();
}

Prevention

When it happens

Trigger: A client sends Sec-WebSocket-Protocol: chat, chat or soap, mqtt, soap. When the parser hits the comma after the second 'chat', it finds 'chat' already in the protocols Set (subprotocol.js:37) and throws. On the server this is caught in handleUpgrade (websocket-server.js:286-292) and aborts the handshake with 400.

Common situations: A client library sends the default protocol plus a user-servised one that collide; a reverse proxy merges multiple client headers by concatenation creating duplicates; a misconfigured client passes the same protocol string twice in its array.

Related errors


AI-assisted analysis of websockets/ws@c791e707ea (2026-08-06). Data as JSON: /api/errors/488b4de7d0ddc989. Report an issue: GitHub.

Appendix: source

Thrown at lib/subprotocol.js:38

    if (end === -1 && tokenChars[code] === 1) {
      if (start === -1) start = i;
    } else if (
      i !== 0 &&
      (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
    ) {
      if (end === -1 && start !== -1) end = i;
    } else if (code === 0x2c /* ',' */) {
      if (start === -1) {
        throw new SyntaxError(`Unexpected character at index ${i}`);
      }

      if (end === -1) end = i;

      const protocol = header.slice(start, end);

      if (protocols.has(protocol)) {
        throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
      }

      protocols.add(protocol);
      start = end = -1;
    } else {
      throw new SyntaxError(`Unexpected character at index ${i}`);
    }
  }

  if (start === -1 || end !== -1) {
    throw new SyntaxError('Unexpected end of input');
  }

  const protocol = header.slice(start, i);

  if (protocols.has(protocol)) {
    throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
  }

View on GitHub (pinned to c791e707ea)