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

  1. Ensure the Sec-WebSocket-Protocol header contains at least one non-empty token and does not end with whitespace.
  2. On the server, handleUpgrade already catches this and responds with HTTP 400 — no additional handling needed for normal usage.
  3. 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

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

Related errors


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)