denoland/deno · error · TypeError
Protocol '${options.protocol}' not in the request's protocol
Error message
Protocol '${options.protocol}' not in the request's protocol list (non negotiable) What it means
Deno.upgradeWebSocket(request, { protocol }) lets the server pick one subprotocol, but only from the list the client offered in 'sec-websocket-protocol'. If options.protocol is not included (exact, case-sensitive ArrayPrototypeIncludes over the comma-split list), Deno refuses rather than silently negotiating a protocol the client did not ask for ('non negotiable').
Source
Thrown at ext/http/02_websocket.ts:86
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, [
"sec-websocket-protocol",
options.protocol,
]);
} else {
throw new TypeError(
`Protocol '${options.protocol}' not in the request's protocol list (non negotiable)`,
);
}
}
const {
_eventLoop,
_idleTimeoutDuration,
_idleTimeoutTimeout,
_readyState,
_rid,
_role,
_serverHandleIdleTimeout,
createWebSocketBranded,
installServerInspector,
SERVER,
WebSocket,
} = loadWebSocket();View on GitHub (pinned to 89f33cbef2)
Solutions
- Pass the same protocol on both sides: client new WebSocket(url, 'chat.v2') and server Deno.upgradeWebSocket(req, { protocol: 'chat.v2' }).
- Or let the server choose from the client's list: read req.headers.get('sec-websocket-protocol'), pick a supported one, and pass that as options.protocol.
- If subprotocols are not needed, omit options.protocol entirely.
- Match exactly - comparison is case-sensitive and against the raw header split on ', '.
Example fix
// before
const { response } = Deno.upgradeWebSocket(req, { protocol: "v2.api" });
// client: new WebSocket(url, ["v1.api"]) -> throws
// after
const offered = (req.headers.get("sec-websocket-protocol") ?? "")
.split(",").map((s) => s.trim());
const protocol = ["v2.api", "v1.api"].find((p) => offered.includes(p));
const { socket, response } = Deno.upgradeWebSocket(req, protocol ? { protocol } : {}); Defensive patterns
Strategy: validation
Validate before calling
const offered = (req.headers.get("sec-websocket-protocol") ?? "")
.split(",").map((s) => s.trim()).filter(Boolean);
const chosen = SUPPORTED_PROTOCOLS.find((p) => offered.includes(p));
const { socket, response } = Deno.upgradeWebSocket(req, chosen ? { protocol: chosen } : {}); Type guard
function pickSubprotocol(req: Request, supported: string[]): string | undefined { const offered = (req.headers.get("sec-websocket-protocol") ?? "").split(",").map((s) => s.trim()); return supported.find((p) => offered.includes(p)); } Try / catch
try { return Deno.upgradeWebSocket(req, { protocol: "chat.v2" }).response; } catch (e) { if (e instanceof TypeError && e.message.includes("protocol list")) { return new Response("unsupported subprotocol", { status: 400 }); } throw e; } Prevention
- Define the subprotocol list in one shared constant used by client and server.
- Remember the match is case-sensitive and taken from the raw comma-split header.
- If the client sent no protocols, don't request one server-side.
When it happens
Trigger: Server hardcodes protocol: 'v2.api' while client sent Sec-WebSocket-Protocol: v1.api or omitted the header; case mismatch ('JSON' vs 'json'); splitting artifact when client sent comma-space vs comma separators expected as ', ' by the server; client and server subprotocol lists diverging after a version bump.
Common situations: GraphQL-over-WS (graphql-transport-ws expects 'graphql-transport-ws'), SOAP-over-WS, or app versioning via subprotocols; forgetting to pass the protocol in new WebSocket(url, ['v1.api']) on the client; renaming a protocol in one component only; header value casing differences between implementations.
Related errors
- Invalid Header: 'upgrade' header must contain 'websocket'
- Invalid Header: 'connection' header must contain 'Upgrade'
- Invalid Header: 'sec-websocket-key' header must be set
- Vary header must not contain '*'
- Already upgraded
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/2b1e2e62913a24b0.
Report an issue: GitHub.