websockets/ws · warning · SyntaxError

Unexpected end of input

Error message

Unexpected end of input

What it means

Thrown by subprotocol.parse() after the loop ends (lib/subprotocol.js:48-50) when start === -1 (no protocol token was ever started, i.e. the header was empty or only whitespace/commas) OR when end !== -1 (a token was terminated by whitespace but the input ended there, i.e. trailing whitespace). Both are malformed per the grammar. On the server this becomes an HTTP 400 'Invalid Sec-WebSocket-Protocol header'.

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 ae1de54330)

Solutions

  1. Do not send the Sec-WebSocket-Protocol header at all if there are no protocols to offer.
  2. Trim trailing/leading whitespace from the header value before sending.
  3. On the server, listen for 'wsClientError' to observe these clients instead of treating them as fatal.

Example fix

// before
req.setHeader('Sec-WebSocket-Protocol', protocols.join(',') + ' ');

// after
req.setHeader('Sec-WebSocket-Protocol', protocols.join(',').trim());
Defensive patterns

Strategy: validation

Validate before calling

function buildProtocolHeader(protocols) {
  const header = [...new Set(protocols.filter(Boolean))].join(',').trim();
  return header.length ? header : undefined; // omit header when empty
}

Type guard

function isNonEmptyTrimmedHeader(header) {
  return typeof header === 'string' && header.trim().length > 0 && header === header.trim();
}

Try / catch

wss.on('wsClientError', (err, socket, req) => {
  if (/Unexpected end of input/.test(err.message)) {
    // empty or trailing-whitespace Sec-WebSocket-Protocol; default 400 already sent
  }
});

Prevention

When it happens

Trigger: A client sends an empty Sec-WebSocket-Protocol header (e.g. '') or a header ending in trailing whitespace like 'chat '. The parser cannot finalize a token and throws SyntaxError('Unexpected end of input').

Common situations: Client libraries that set the header unconditionally even when no protocols are configured; trailing-whitespace bugs from string concatenation; proxies that strip content but leave the empty header.

Related errors


AI-assisted analysis of websockets/ws@ae1de54330 (2026-08-03). Data as JSON: /data/errors/0a6f40068860f199.json. Report an issue: GitHub.