Skip to content

Name a broken answer body instead of "error decoding response body" - #344

Merged
fylorn merged 1 commit into
mainfrom
fix/body-read-error-cause
Oct 17, 2026
Merged

fylorn merged 1 commit into
mainfrom
fix/body-read-error-cause

Conversation

@fylorn

@fylorn fylorn commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Problem

reqwest 0.13 maps every error while reading a response body to Kind::Decode, whose Display is only error decoding response body. map_reqwest_error formatted the error with Display alone, so an upstream (or a proxy in between) that closed the connection mid-answer showed up as Forwarding failed: error decoding response body. That reads like a decoding bug, and the real cause (end of file before message length reached, connection reset by peer, an HTTP/2 stream reset, ...) only lives in source() and was lost.

Change

  • New code gw.upstream.body_broken: "The connection to the upstream broke while the answer was being received: {detail}", where detail is the cause chain. A decode error whose chain holds a serde_json::Error (a json() parse failure) is not treated as a broken connection.
  • The extra reqwest layer that the replayed opening (opening.rs, Body::wrap_stream) adds is skipped, so the cause is not preceded by a second "error decoding response body".
  • gw.upstream.forward_failed now carries every cause as well; a URL in the top-level message is reduced to its origin with tw_secret::redact_url.
  • gw.upstream.body_broken is added to FAILED_AFTER_SENDING (the upstream did receive the request); msg-codes.txt regenerated.

The code is new, so Lite needs a Chinese sentence for it when it next bumps its core pin (its msg_codes test will require it).

Tests

  • forward::tests: a body cut short, the same break behind a replayed opening, and the cause chain with the URL redacted.
  • passthrough::a_stream_that_dies_midway_says_so_instead_of_just_stopping now also checks the code and the cause.

🤖 Generated with Claude Code

reqwest 0.13 reports every failure while reading a response body as a
decode error, so an upstream that hung up mid-answer reached the UI as
"Forwarding failed: error decoding response body", which reads like a
decoding bug and drops the actual cause (the source chain).

- Body-read failures get their own code, gw.upstream.body_broken, with
  the causes spelled out (e.g. "error reading a body from connection:
  end of file before message length reached"). The extra reqwest layer
  added by the replayed opening is skipped so the cause is told once.
- gw.upstream.forward_failed now carries every cause too, with the URL
  reduced to its origin.
- gw.upstream.body_broken is listed in FAILED_AFTER_SENDING.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn
fylorn merged commit 25aa2b6 into main Oct 17, 2026
6 checks passed
@fylorn
fylorn deleted the fix/body-read-error-cause branch October 17, 2026 03:30
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant