{"record":{"id":"488b4de7d0ddc989","repo":"websockets/ws","slug":"the-protocol-subprotocol-is-duplicated","errorCode":null,"errorMessage":"The \"${protocol}\" subprotocol is duplicated","messagePattern":"The \"(.+?)\" subprotocol is duplicated","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"warning","filePath":"lib/subprotocol.js","lineNumber":38,"sourceCode":"\n    if (end === -1 && tokenChars[code] === 1) {\n      if (start === -1) start = i;\n    } else if (\n      i !== 0 &&\n      (code === 0x20 /* ' ' */ || code === 0x09) /* '\\t' */\n    ) {\n      if (end === -1 && start !== -1) end = i;\n    } else if (code === 0x2c /* ',' */) {\n      if (start === -1) {\n        throw new SyntaxError(`Unexpected character at index ${i}`);\n      }\n\n      if (end === -1) end = i;\n\n      const protocol = header.slice(start, end);\n\n      if (protocols.has(protocol)) {\n        throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n      }\n\n      protocols.add(protocol);\n      start = end = -1;\n    } else {\n      throw new SyntaxError(`Unexpected character at index ${i}`);\n    }\n  }\n\n  if (start === -1 || end !== -1) {\n    throw new SyntaxError('Unexpected end of input');\n  }\n\n  const protocol = header.slice(start, i);\n\n  if (protocols.has(protocol)) {\n    throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n  }","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/websockets/ws/blob/c791e707eab3c13dd9a261d2479c3cc4a49a6fed/lib/subprotocol.js#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dedupe the protocol list on the client before connecting: [...new Set(protocols)].join(',').","If using the ws client constructor, pass a de-duplicated array: new WebSocket(url, [...new Set(protocols)]).","On the server, no action needed — handleUpgrade already catches and rejects with HTTP 400."],"exampleFix":"// before\nconst ws = new WebSocket(url, ['chat', 'chat', 'json']);\n\n// after\nconst ws = new WebSocket(url, [...new Set(['chat', 'chat', 'json'])]);","handlingStrategy":"validation","validationCode":"// Client-side: dedupe before sending\nfunction uniqueProtocols(protocols) {\n  return [...new Set(protocols.filter(p => typeof p === 'string' && p.length > 0))];\n}\n// usage: new WebSocket(url, uniqueProtocols(list));","typeGuard":"function hasNoDuplicateProtocols(protocols) {\n  return new Set(protocols).size === protocols.length;\n}","tryCatchPattern":"const { parse } = require('ws/lib/subprotocol');\ntry {\n  protocols = parse(header);\n} catch (err) {\n  if (/duplicated/.test(err.message)) {\n    // client sent a duplicate protocol; reject\n  }\n  socket.destroy();\n}","preventionTips":["Dedupe protocol lists with a Set before joining or passing to the constructor.","On the server, handleUpgrade already rejects duplicate-laden headers with 400.","Avoid concatenating protocol headers in proxies without deduplication."],"tags":["websocket","subprotocol","header-parsing","rfc6455","deduplication"],"backgroundTag":null,"analyzedSha":"c791e707eab3c13dd9a261d2479c3cc4a49a6fed","analyzedAt":"2026-08-06T19:07:51.047Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}