dotnet/yarp · error · Exception
Read {read} bytes, expected 0 after sending Goodbye.
Error message
Read {read} bytes, expected 0 after sending Goodbye. What it means
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.
Source
Thrown at testassets/TestClient/Scenarios/RawUpgradeScenario.cs:52
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.");
}
Console.WriteLine();
}
else
{
if (read != 1)
{
throw new Exception($"Read {read} bytes, expected 1.");
}
if (buffer[0] != i)
{
throw new Exception($"Received {buffer[0]}, expected {i}.");
}
Console.Write(".");
}View on GitHub (pinned to bd11867bee)
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.
Defensive patterns
Strategy: try-catch
Try / catch
try { await RawUpgradeScenario.RunAsync(client, args, cancellation); }
catch (Exception ex) when (ex.Message.Contains("expected 0 after sending Goodbye")) {
Console.Error.WriteLine("Backend did not stop echoing after Goodbye; use the matching echo test server.");
return 1;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: Backend echo implementation that mirrors every byte including the goodbye; partial network read; the upgraded stream being half-closed differently than the scenario assumes.
Related errors
- Expected status 101 Switching Protocols!
- Read {read} bytes, expected 1.
- Received {buffer[0]}, expected {i}.
- Expected status 409 Conflict!
- Expected to receive a text message, got '{message.MessageTyp
AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13).
Data as JSON: /api/errors/d0a4f53c5acc86b4.
Report an issue: GitHub.