websockets/ws · error · SyntaxError
Unexpected character at index
Error message
Unexpected character at index ${i} What it means
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.
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.
Example fix
// before
const offers = extension.parse(';permessage-deflate');
// after
const offers = extension.parse('permessage-deflate'); Defensive patterns
Strategy: try-catch
Try / catch
const { parse } = require('ws/lib/extension');
try {
const offers = parse(header);
} catch (err) {
if (err instanceof SyntaxError) {
// Malformed Sec-WebSocket-Extensions header — fail the handshake.
socket.write(Buffer.from([0x88, 0x02, 0x03, 0xea])); // close 1002
socket.destroy();
return;
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected end of input
- First argument must be a valid error code number
- Parameter " " must have only a single value
- The data size must not be greater than 125 bytes
- The message must not be greater than 123 bytes
AI-assisted analysis of websockets/ws@c791e707ea (2026-08-06).
Data as JSON: /api/errors/1894de79dc16fe79.
Report an issue: GitHub.
Appendix: source
Thrown at lib/extension.js:53
let start = -1;
let code = -1;
let end = -1;
let i = 0;
for (; i < header.length; i++) {
code = header.charCodeAt(i);
if (extensionName === undefined) {
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 === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = Object.create(null);
} else {
extensionName = name;
}
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (paramName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;View on GitHub (pinned to c791e707ea)