{"record":{"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/c791e707eab3c13dd9a261d2479c3cc4a49a6fed/lib/extension.js#L35-L71","documentation":"Thrown by `extension.parse()` while parsing the `Sec-WebSocket-Extensions` header (RFC 6455 §9.1). At this point the parser expects an extension name token (no name has started yet, `extensionName === undefined`) but it hit a `;` (0x3b) or `,` (0x2c) separator with `start === -1`, meaning the separator appeared before any token characters were read — e.g. the header begins with a separator, or two separators appear back-to-back at the start. The index `i` in the message is the byte offset of the offending character.","triggerScenarios":"Calling `extension.parse(';permessage-deflate')`, `extension.parse(',permessage-deflate')`, or any header where a `;`/`,` appears before the first extension-name token. The library calls this internally when negotiating `permessage-deflate` against a malformed peer header.","commonSituations":"A buggy client/server sends a hand-crafted `Sec-WebSocket-Extensions` value with a leading separator; a proxy rewrites the header and injects a stray `;`; manual testing with a malformed header string.","solutions":["Inspect the offending header at the reported index and strip/escape the leading separator.","Do not hand-build the header; produce it with `extension.format()` which always yields syntactically valid output.","If the value comes from an untrusted peer, wrap the `accept()`/`parse()` handshake call in try/catch and fail the connection with a 1002 protocol error."],"exampleFix":"// before\nconst offers = extension.parse(';permessage-deflate');\n\n// after\nconst offers = extension.parse('permessage-deflate');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const { parse } = require('ws/lib/extension');\ntry {\n  const offers = parse(header);\n} catch (err) {\n  if (err instanceof SyntaxError) {\n    // Malformed Sec-WebSocket-Extensions header — fail the handshake.\n    socket.write(Buffer.from([0x88, 0x02, 0x03, 0xea])); // close 1002\n    socket.destroy();\n    return;\n  }\n  throw err;\n}","preventionTips":["Never build `Sec-WebSocket-Extensions` by string concatenation; use `extension.format()`.","Treat any SyntaxError from `parse()`/`accept()` during the handshake as a protocol error and close with code 1002.","Log the offending header (and the index from the message) to diagnose peer/proxy corruption."],"tags":["websocket","protocol","parsing","header","rfc6455","sec-websocket-extensions"],"backgroundTag":null,"analyzedSha":"c791e707eab3c13dd9a261d2479c3cc4a49a6fed","analyzedAt":"2026-08-06T19:07:51.047Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}