{"record":{"id":"d0a4f53c5acc86b4","repo":"dotnet/yarp","slug":"read-read-bytes-expected-0-after-sending-goodby","errorCode":null,"errorMessage":"Read {read} bytes, expected 0 after sending Goodbye.","messagePattern":"Read (.+?) bytes, expected 0 after sending Goodbye\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"testassets/TestClient/Scenarios/RawUpgradeScenario.cs","lineNumber":52,"sourceCode":"        if (response.StatusCode != HttpStatusCode.SwitchingProtocols)\n        {\n            throw new InvalidOperationException(\"Expected status 101 Switching Protocols!\");\n        }\n\n        var rawStream = await response.Content.ReadAsStreamAsync(cancellation);\n        Console.WriteLine(\"Acquired upgraded stream. Testing bidirectional echo...\");\n        stopwatch.Restart();\n        var buffer = new byte[1];\n        for (var i = 0; i <= 255; i++)\n        {\n            buffer[0] = (byte)i;\n            await rawStream.WriteAsync(buffer, cancellation);\n            var read = await rawStream.ReadAsync(buffer, cancellation);\n            if (i == 255)\n            {\n                if (read != 0)\n                {\n                    throw new Exception($\"Read {read} bytes, expected 0 after sending Goodbye.\");\n                }\n\n                Console.WriteLine();\n            }\n            else\n            {\n                if (read != 1)\n                {\n                    throw new Exception($\"Read {read} bytes, expected 1.\");\n                }\n\n                if (buffer[0] != i)\n                {\n                    throw new Exception($\"Received {buffer[0]}, expected {i}.\");\n                }\n\n                Console.Write(\".\");\n            }","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/testassets/TestClient/Scenarios/RawUpgradeScenario.cs#L34-L70","documentation":"Asserted at the end of RawUpgradeScenario's echo loop. After writing byte value 255 (the agreed 'Goodbye' marker), the scenario expects the server to close/stop echoing, so ReadAsync should report 0 bytes read. A non-zero read means the server kept echoing or did not recognize the goodbye, so an Exception is thrown with the actual byte count.","triggerScenarios":"Running RawUpgradeScenario against a raw-upgrade endpoint that does not stop echoing after receiving byte 255, or where the stream semantics differ (e.g. it echoes the final byte too).","commonSituations":"Backend echo implementation that mirrors every byte including the goodbye; partial network read; the upgraded stream being half-closed differently than the scenario assumes.","solutions":["Use the matching test backend that stops echoing after byte 255 and returns 0.","Confirm the full 256-byte exchange completed before this point (errors 94/95 would fire first if echo was broken).","Check the connection was not reset midway, which can surface as unexpected reads."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await RawUpgradeScenario.RunAsync(client, args, cancellation); }\ncatch (Exception ex) when (ex.Message.Contains(\"expected 0 after sending Goodbye\")) {\n    Console.Error.WriteLine(\"Backend did not stop echoing after Goodbye; use the matching echo test server.\");\n    return 1;\n}","preventionTips":["Run the scenario only against the matching echo backend that stops after byte 255.","Treat errors 94/95 firing first as the primary signal; this error implies the earlier loop was healthy.","Keep the upgraded stream free of transforming intermediaries."],"tags":["test","http-upgrade","scenario","test-client","yarp"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}