dotnet/yarp · error · InvalidOperationException

Expected status 101 Switching Protocols!

Error message

Expected status 101 Switching Protocols!

What it means

Asserted by RawUpgradeScenario in the SampleClient. It sends an HTTP/1.1 request with Connection: upgrade to /api/rawupgrade and expects a 101 Switching Protocols response so it can take over the raw stream for a bidirectional echo test. Anything other than 101 throws InvalidOperationException, meaning the upgrade was not negotiated.

Source

Thrown at testassets/TestClient/Scenarios/RawUpgradeScenario.cs:36

        {
            AllowAutoRedirect = false,
            AutomaticDecompression = DecompressionMethods.None,
            UseCookies = false,
            UseProxy = false
        };
        using var client = new HttpMessageInvoker(handler);
        var targetUri = new Uri(new Uri(args.Target, UriKind.Absolute), "api/rawupgrade");
        var stopwatch = Stopwatch.StartNew();
        var request = new HttpRequestMessage(HttpMethod.Get, targetUri);
        request.Headers.TryAddWithoutValidation("Connection", "upgrade");
        request.Version = new Version(1, 1);
        Console.WriteLine($"Calling {targetUri} with upgradable HTTP/1.1");

        var response = await client.SendAsync(request, cancellation);
        Console.WriteLine($"Received response: {(int)response.StatusCode} in {stopwatch.ElapsedMilliseconds} ms");
        if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
        {
            throw new InvalidOperationException("Expected status 101 Switching Protocols!");
        }

        var rawStream = await response.Content.ReadAsStreamAsync(cancellation);
        Console.WriteLine("Acquired upgraded stream. Testing bidirectional echo...");
        stopwatch.Restart();
        var buffer = new byte[1];
        for (var i = 0; i <= 255; i++)
        {
            buffer[0] = (byte)i;
            await rawStream.WriteAsync(buffer, cancellation);
            var read = await rawStream.ReadAsync(buffer, cancellation);
            if (i == 255)
            {
                if (read != 0)
                {
                    throw new Exception($"Read {read} bytes, expected 0 after sending Goodbye.");
                }

View on GitHub (pinned to bd11867bee)

Solutions

  1. Target the matching test server that implements /api/rawupgrade with a 101 response.
  2. Ensure no intermediary strips the Connection or Upgrade headers.
  3. Verify the request is sent as HTTP/1.1 (the scenario forces it, but a misconfigured handler could alter behavior).
  4. Inspect the printed received status to diagnose (e.g. 200 means upgrade was ignored).
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe upgrade support before the scenario.
using var probe = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Get, new Uri(args.Target, "api/rawupgrade"));
req.Headers.TryAddWithoutValidation("Connection", "upgrade");
req.Version = new Version(1, 1);
var resp = await probe.SendAsync(req);
if (resp.StatusCode != HttpStatusCode.SwitchingProtocols)
    Console.Error.WriteLine($"Warning: /api/rawupgrade returned {resp.StatusCode}, scenario will fail.");

Try / catch

try { await RawUpgradeScenario.RunAsync(client, args, cancellation); }
catch (InvalidOperationException ex) when (ex.Message.Contains("101 Switching Protocols")) {
    Console.Error.WriteLine("Raw upgrade not negotiated; check --target and intermediaries.");
    return 1;
}

Prevention

When it happens

Trigger: Running SampleClient --scenario RawUpgradeScenario against a server that does not return 101 for /api/rawupgrade, or where an intermediary refuses the upgrade.

Common situations: Wrong --target; the rawupgrade test endpoint missing; a reverse proxy/CDN in front that downgrades or drops the Connection: upgrade header; HTTP/1.0 target that cannot switch protocols.

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/df5b4c316ba0352e. Report an issue: GitHub.