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

  1. Pass the same protocol on both sides: client new WebSocket(url, 'chat.v2') and server Deno.upgradeWebSocket(req, { protocol: 'chat.v2' }).
  2. 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.
  3. If subprotocols are not needed, omit options.protocol entirely.
  4. 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

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


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