{"id":"0a6f40068860f199","repo":"websockets/ws","slug":"unexpected-end-of-input-0a6f40","errorCode":null,"errorMessage":"Unexpected end of input","messagePattern":"Unexpected end of input","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"warning","filePath":"lib/subprotocol.js","lineNumber":49,"sourceCode":"      }\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  }\n\n  protocols.add(protocol);\n  return protocols;\n}\n\nmodule.exports = { parse };\n","sourceCodeStart":31,"sourceCodeEnd":63,"githubUrl":"https://github.com/websockets/ws/blob/ae1de54330cef77e487548890fabfeb9aae1d83d/lib/subprotocol.js#L31-L63","documentation":"Thrown by subprotocol.parse() after the loop ends (lib/subprotocol.js:48-50) when start === -1 (no protocol token was ever started, i.e. the header was empty or only whitespace/commas) OR when end !== -1 (a token was terminated by whitespace but the input ended there, i.e. trailing whitespace). Both are malformed per the grammar. On the server this becomes an HTTP 400 'Invalid Sec-WebSocket-Protocol header'.","triggerScenarios":"A client sends an empty Sec-WebSocket-Protocol header (e.g. '') or a header ending in trailing whitespace like 'chat '. The parser cannot finalize a token and throws SyntaxError('Unexpected end of input').","commonSituations":"Client libraries that set the header unconditionally even when no protocols are configured; trailing-whitespace bugs from string concatenation; proxies that strip content but leave the empty header.","solutions":["Do not send the Sec-WebSocket-Protocol header at all if there are no protocols to offer.","Trim trailing/leading whitespace from the header value before sending.","On the server, listen for 'wsClientError' to observe these clients instead of treating them as fatal."],"exampleFix":"// before\nreq.setHeader('Sec-WebSocket-Protocol', protocols.join(',') + ' ');\n\n// after\nreq.setHeader('Sec-WebSocket-Protocol', protocols.join(',').trim());","handlingStrategy":"validation","validationCode":"function buildProtocolHeader(protocols) {\n  const header = [...new Set(protocols.filter(Boolean))].join(',').trim();\n  return header.length ? header : undefined; // omit header when empty\n}","typeGuard":"function isNonEmptyTrimmedHeader(header) {\n  return typeof header === 'string' && header.trim().length > 0 && header === header.trim();\n}","tryCatchPattern":"wss.on('wsClientError', (err, socket, req) => {\n  if (/Unexpected end of input/.test(err.message)) {\n    // empty or trailing-whitespace Sec-WebSocket-Protocol; default 400 already sent\n  }\n});","preventionTips":["Do not send Sec-WebSocket-Protocol when the protocol list is empty.","Trim leading/trailing whitespace from the header value before sending.","Watch for middleware that leaves an empty header after stripping content."],"tags":["websocket","subprotocol","handshake","header-parsing"],"analyzedSha":"ae1de54330cef77e487548890fabfeb9aae1d83d","analyzedAt":"2026-08-03T19:11:18.437Z","schemaVersion":2}