websockets/ws · warning · SyntaxError
Unexpected end of input
Error message
Unexpected end of input
What it means
Thrown by subprotocol.parse() at subprotocol.js:48-49 after the loop ends, when the header is structurally incomplete. Two cases: (a) start === -1, meaning no token was ever started (empty header or whitespace-only header), or (b) end !== -1, meaning a token was terminated by whitespace but the header ended without a final token or comma (e.g. trailing space, or a header like 'chat ').
Solutions
- Ensure the Sec-WebSocket-Protocol header contains at least one non-empty token and does not end with whitespace.
- On the server, handleUpgrade already catches this and responds with HTTP 400 — no additional handling needed for normal usage.
- If calling parse() directly, wrap in try/catch and reject malformed/empty headers.
Example fix
// before ws = new WebSocket(url, ' '); // after — omit the header or provide a real token ws = new WebSocket(url, 'chat');
Defensive patterns
Strategy: try-catch
Validate before calling
function isValidProtocolHeader(header) {
if (typeof header !== 'string') return false;
const trimmed = header.trim();
if (trimmed.length === 0) return false;
try { require('ws/lib/subprotocol').parse(trimmed); return true; }
catch { return false; }
} Type guard
function isNonEmptyProtocolHeader(header) {
return typeof header === 'string' && header.trim().length > 0;
} Try / catch
try {
protocols = subprotocol.parse(secWebSocketProtocol);
} catch (err) {
// empty/whitespace-only or structurally incomplete header
abortHandshake(socket, 400);
return;
} Prevention
- Never send an empty or whitespace-only Sec-WebSocket-Protocol header; omit the header instead.
- Trim trailing whitespace when constructing header values.
- Wrap any direct call to subprotocol.parse() in try/catch and reject on failure.
When it happens
Trigger: A client sends an empty Sec-WebSocket-Protocol header (or just spaces/tabs), or a header that ends with trailing whitespace after a token like 'chat '. After the loop, start === -1 (nothing started) or end !== -1 (token was whitespace-terminated but input ended) triggers the error at subprotocol.js:49.
Common situations: A header value of '' or ' ' (whitespace only); a trailing-space bug when constructing headers; a proxy that strips content but leaves the header field present; an intermediate layer that sets the header to a space.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- The " " subprotocol is duplicated
- Unexpected character at index
- An invalid or duplicated subprotocol was specified
- 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/0a6f40068860f199.
Report an issue: GitHub.
Appendix: source
Thrown at lib/subprotocol.js:49
}
if (end === -1) end = i;
const protocol = header.slice(start, end);
if (protocols.has(protocol)) {
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
}
protocols.add(protocol);
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
}
if (start === -1 || end !== -1) {
throw new SyntaxError('Unexpected end of input');
}
const protocol = header.slice(start, i);
if (protocols.has(protocol)) {
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
}
protocols.add(protocol);
return protocols;
}
module.exports = { parse };
View on GitHub (pinned to c791e707ea)