websockets/ws · error · SyntaxError

Unexpected end of input

Error message

Unexpected end of input

What it means

Thrown by `extension.parse()` after the loop ends, when the final state is incomplete: `start === -1` (the parser was mid-way expecting another token but got end-of-input), `inQuotes === true` (an unterminated quoted value), or the last consumed character was a space/tab (trailing whitespace after a value that needed a separator or end). The message has no index because the failure is at EOF.

Solutions

  1. Terminate every quoted value with a closing `"` and ensure the header ends with a complete token.
  2. Remove trailing separators and trailing whitespace.
  3. Catch the SyntaxError and close the connection with code 1002.

Example fix

// before — unterminated quoted value
const offers = extension.parse('permessage-deflate; key="unterminated');

// after — close the quote
const offers = extension.parse('permessage-deflate; key="terminated"');
Defensive patterns

Strategy: try-catch

Try / catch

const { parse } = require('ws/lib/extension');
try {
  const offers = parse(header);
} catch (err) {
  if (err instanceof SyntaxError) {
    // Truncated header: unterminated quote, trailing separator, or trailing space.
    abortHandshake(socket, 1002);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `extension.parse('permessage-deflate; key="unterminated')` (unclosed quote), `extension.parse('permessage-deflate;')` (trailing separator with nothing after), or `extension.parse('permessage-deflate; key=value ')` (trailing space).

Common situations: A peer truncates the header mid-value (MTU/proxy bug); a hand-edited header left unterminated; a trailing space from string concatenation.

Understand the failure class

Related errors


AI-assisted analysis of websockets/ws@c791e707ea (2026-08-06). Data as JSON: /api/errors/09e75ef415ffc20c. Report an issue: GitHub.

Appendix: source

Thrown at lib/extension.js:151

          mustUnescape = false;
        }
        push(params, paramName, value);
        if (code === 0x2c) {
          push(offers, extensionName, params);
          params = Object.create(null);
          extensionName = undefined;
        }

        paramName = undefined;
        start = end = -1;
      } else {
        throw new SyntaxError(`Unexpected character at index ${i}`);
      }
    }
  }

  if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
    throw new SyntaxError('Unexpected end of input');
  }

  if (end === -1) end = i;
  const token = header.slice(start, end);
  if (extensionName === undefined) {
    push(offers, token, params);
  } else {
    if (paramName === undefined) {
      push(params, token, true);
    } else if (mustUnescape) {
      push(params, paramName, token.replace(/\\/g, ''));
    } else {
      push(params, paramName, token);
    }
    push(offers, extensionName, params);
  }

  return offers;

View on GitHub (pinned to c791e707ea)