{"id":"1894de79dc16fe79","repo":"websockets/ws","slug":"unexpected-character-at-index-i","errorCode":null,"errorMessage":"Unexpected character at index ${i}","messagePattern":"Unexpected character at index (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"lib/extension.js","lineNumber":53,"sourceCode":"  let start = -1;\n  let code = -1;\n  let end = -1;\n  let i = 0;\n\n  for (; i < header.length; i++) {\n    code = header.charCodeAt(i);\n\n    if (extensionName === undefined) {\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 === 0x3b /* ';' */ || code === 0x2c /* ',' */) {\n        if (start === -1) {\n          throw new SyntaxError(`Unexpected character at index ${i}`);\n        }\n\n        if (end === -1) end = i;\n        const name = header.slice(start, end);\n        if (code === 0x2c) {\n          push(offers, name, params);\n          params = Object.create(null);\n        } else {\n          extensionName = name;\n        }\n\n        start = end = -1;\n      } else {\n        throw new SyntaxError(`Unexpected character at index ${i}`);\n      }\n    } else if (paramName === undefined) {\n      if (end === -1 && tokenChars[code] === 1) {\n        if (start === -1) start = i;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/websockets/ws/blob/ae1de54330cef77e487548890fabfeb9aae1d83d/lib/extension.js#L35-L71","documentation":"Thrown by extension.parse() while reading an extension NAME in the Sec-WebSocket-Extensions header. The parser hit a ';' or ',' separator but start was -1, meaning no token characters had been accumulated where an extension name was expected (extension.js:51-53). This violates the RFC 6455 §9.1 ABNF which requires at least one token char before any separator.","triggerScenarios":"Calling WebSocket.extension.parse(header) (exported as WebSocket.extension.parse) with a header string that begins with or contains a stray separator in the extension-name position, e.g. ';permessage-deflate', 'permessage-deflate,,x', or 'permessage-deflate,;x'. A malformed Sec-WebSocket-Extensions header received from a peer would also trigger this through the internal handshake path.","commonSituations":"A hand-crafted or buggy client sends a Sec-WebSocket-Extensions header with a leading/trailing/doubled separator. A proxy or middleware injects a malformed extensions header. A developer testing extension negotiation feeds a syntactically broken string to parse().","solutions":["Inspect the raw Sec-WebSocket-Extensions header value and fix the malformed separator placement (remove leading/doubled/stray ';' and ',').","If you are calling parse() directly, sanitize or reject the header string before parsing.","If received from a peer, the ws server/client internally catches this and aborts the handshake with HTTP 400 — ensure you are not bypassing that internal handler.","Upgrade or patch the peer that is generating the non-compliant header."],"exampleFix":"// before\nconst offers = WebSocket.extension.parse(';permessage-deflate');\n\n// after\nconst offers = WebSocket.extension.parse('permessage-deflate');","handlingStrategy":"try-catch","validationCode":"// Validate that the header does not start with or double-up separators\nfunction isValidExtensionsHeader(header) {\n  if (typeof header !== 'string') return false;\n  if (/^[;\\s,]|[;,][;,]|[;,]$/.test(header.trim())) return false;\n  return true;\n}\n\nif (isValidExtensionsHeader(header)) {\n  const offers = WebSocket.extension.parse(header);\n}","typeGuard":"function isNonEmptyHeader(header) {\n  return typeof header === 'string' && header.trim().length > 0;\n}","tryCatchPattern":"try {\n  const offers = WebSocket.extension.parse(header);\n} catch (err) {\n  if (err instanceof SyntaxError) {\n    // Malformed Sec-WebSocket-Extensions header — reject/log\n    console.error('Bad extensions header:', err.message);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Never manually construct Sec-WebSocket-Extensions header strings — use the extension.format() helper.","If calling extension.parse() directly, wrap it in try-catch and treat SyntaxError as a malformed-input signal.","In server mode, rely on the internal handshake handler (websocket-server.js) which catches parse errors and returns HTTP 400 automatically.","Sanitize peer-supplied headers before debugging with parse()."],"tags":["websocket","extension","handshake","header-parsing","rfc6455"],"analyzedSha":"ae1de54330cef77e487548890fabfeb9aae1d83d","analyzedAt":"2026-08-03T19:11:18.437Z","schemaVersion":2}