Skip to content

service: surface streaming request body read errors to handlers#150

Open
EffortlessSteven wants to merge 1 commit into
anthropics:mainfrom
EffortlessSteven:claude/stream-body-error
Open

service: surface streaming request body read errors to handlers#150
EffortlessSteven wants to merge 1 commit into
anthropics:mainfrom
EffortlessSteven:claude/stream-body-error

Conversation

@EffortlessSteven
Copy link
Copy Markdown

What this does

A request body transport failure (Body::poll_frame returning Err) mid-decode made spawn_body_reader stop, ending the handler's request stream with a clean EOF. A truncated client-streaming/bidi upload was then indistinguishable from a complete stream, so a handler aggregating messages treated partial input as a finished stream.

Fix: while still decoding, forward the failure to the handler stream as ConnectError::internal (BodyReader::on_body_error), the same error the unary body-read path already returns. Once only draining trailing bytes (after END_STREAM, a decode error, or a dropped receiver), it stays diagnostic-only.

Verification

Red on main, green after: unpatched, the decoding-path test sees None where an Err belongs.

Check Result
body error while decoding unpatched None (clean EOF), patched Err(Internal) after the message
body error after END_STREAM clean end, no spurious Err
cargo +nightly fmt --all --check clean
cargo clippy -p connectrpc --all-features --all-targets -- -D warnings clean
cargo test -p connectrpc --all-features 410 passed

Review map

  • connectrpc/src/service.rs: on_body_error surfaces the failure while Decoding, suppresses it while Draining; read loop calls it instead of dropping the cause.
  • connectrpc/src/service.rs (tests): pin the surfaced and suppressed paths.

`spawn_body_reader` logged `Body::poll_frame` errors and stopped the
reader, so the handler-facing request stream saw a clean EOF when the
HTTP request body failed mid-decode. A truncated or aborted
client-streaming/bidi request was then indistinguishable from a
complete client stream.

Forward transport-level body read failures to the handler stream as
`ConnectError::internal` while the reader is still decoding request
messages (`BodyReader::on_body_error`), matching the unary body-read
path. Once the reader is only draining trailing bytes (after END_STREAM,
a decode error, or the handler dropping the request stream), the error
stays diagnostic-only, preserving the END_STREAM-as-terminal drain
contract.

Adds regression coverage for both the surfaced (decoding) and suppressed
(draining) paths.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@EffortlessSteven
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant