denoland/deno · error · TypeError

Invalid Header: 'sec-websocket-key' header must be set

Error message

Invalid Header: 'sec-websocket-key' header must be set

What it means

Third handshake check in Deno.upgradeWebSocket: the 'sec-websocket-key' header must be present (non-null). This base64 16-byte nonce is required by RFC 6455 and is also fed to op_http_websocket_accept_header to compute the Sec-WebSocket-Accept reply, so a missing key cannot be defaulted.

Source

Thrown at ext/http/02_websocket.ts:63

    websocketCvf(upgrade);
  if (!upgradeHasWebSocketOption) {
    throw new TypeError(
      "Invalid Header: 'upgrade' header must contain 'websocket'",
    );
  }

  const connection = request.headers.get("connection");
  const connectionHasUpgradeOption = connection !== null &&
    upgradeCvf(connection);
  if (!connectionHasUpgradeOption) {
    throw new TypeError(
      "Invalid Header: 'connection' header must contain 'Upgrade'",
    );
  }

  const websocketKey = request.headers.get("sec-websocket-key");
  if (websocketKey === null) {
    throw new TypeError(
      "Invalid Header: 'sec-websocket-key' header must be set",
    );
  }

  const accept = op_http_websocket_accept_header(websocketKey);

  const r = newInnerResponse(101);
  r.headerList = [
    ["upgrade", "websocket"],
    ["connection", "Upgrade"],
    ["sec-websocket-accept", accept],
  ];

  const protocolsStr = request.headers.get("sec-websocket-protocol") || "";
  const protocols = StringPrototypeSplit(protocolsStr, ", ");
  if (protocols && options.protocol) {
    if (ArrayPrototypeIncludes(protocols, options.protocol)) {
      ArrayPrototypePush(r.headerList, [

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Use a real WebSocket client (browser WebSocket, ws package, or Deno's WebSocket) which always sends the key.
  2. If testing by hand, include a valid key: curl -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' ...
  3. Check proxies/agents for header stripping and whitelist sec-websocket-* headers.
  4. Pre-check req.headers.has('sec-websocket-key') before calling upgradeWebSocket and answer 400 otherwise.

Example fix

// before
// hand-rolled client omitted Sec-WebSocket-Key; server:
const { response } = Deno.upgradeWebSocket(req); // throws

// after
if (!req.headers.has("sec-websocket-key")) {
  return new Response("missing sec-websocket-key", { status: 400 });
}
const { socket, response } = Deno.upgradeWebSocket(req);
Defensive patterns

Strategy: validation

Validate before calling

if (!req.headers.has("sec-websocket-key")) {
  return new Response("missing sec-websocket-key", { status: 400 });
}

Type guard

function hasWsKey(req: Request): boolean { return req.headers.has("sec-websocket-key"); }

Try / catch

try { return Deno.upgradeWebSocket(req).response; } catch (e) { if (e instanceof TypeError && e.message.includes("sec-websocket-key")) { return new Response("bad websocket handshake", { status: 400 }); } throw e; }

Prevention

When it happens

Trigger: Hand-rolled clients or test scripts that send Upgrade/Connection but omit Sec-WebSocket-Key; proxies stripping less common headers; malformed requests replayed from captured traffic where the header was redacted; HTTP/1.0-style requests.

Common situations: Custom embedded clients implementing WS by hand; security appliances filtering WebSocket headers; integration tests using raw TCP/HTTP sockets; curl-based smoke tests that only mimic the two obvious headers.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/934a66b8449aaeba. Report an issue: GitHub.