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
- Use a real WebSocket client (browser WebSocket, ws package, or Deno's WebSocket) which always sends the key.
- 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' ...
- Check proxies/agents for header stripping and whitelist sec-websocket-* headers.
- 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
- Use spec-compliant WS clients (browser WebSocket, ws, Deno.connect-backed).
- When hand-testing with curl, include Sec-WebSocket-Key/Version headers.
- Ensure middleboxes don't strip sec-* headers.
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
- Invalid Header: 'upgrade' header must contain 'websocket'
- Invalid Header: 'connection' header must contain 'Upgrade'
- Protocol '${options.protocol}' not in the request's protocol
- Vary header must not contain '*'
- Already upgraded
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/934a66b8449aaeba.
Report an issue: GitHub.