fix: wrap decoder exceptions as DecodeError at seam B#32
Merged
Conversation
Closes the gap where pydantic.ValidationError / msgspec.ValidationError escaped `except httpware.ClientError` when `response_model=` was set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drives the spec at planning/specs/2026-06-07-decoder-error-design.md into 9 bite-sized tasks: errors.py class, public re-export, both send() wraps, msgspec seam parity, protocol docstring, errors.md + engineering.md + README docs, final verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DecodeError is a direct child of ClientError carrying the response, model, and original library exception. Construction-only here; the client.send wrap follows in the next commit.
Adds DecodeError to httpware.__init__'s errors import block and __all__, plus the explicit expected-exports test. No behavior change yet.
Both Client.send and AsyncClient.send now translate any Exception raised by the active ResponseDecoder into httpware.DecodeError, so `except httpware.ClientError` covers the response_model= path uniformly regardless of which decoder is wired in. Drops the previous test_decoder_validation_error_propagates_unwrapped case which encoded the now-fixed leak.
Seam-level test wires MsgspecDecoder into AsyncClient and asserts a malformed-JSON response still surfaces as httpware.DecodeError with exc.original carrying the underlying msgspec exception.
One-sentence addition: implementers can raise whatever their backing library raises; Client.send / AsyncClient.send translate to httpware.DecodeError at the seam.
Adds DecodeError to the exception-tree diagram and a new subsection covering when it's raised, what fields it carries, and a minimal except snippet.
Updates the Seam B contract to spell out the wrap, and adds a paragraph to the §4 exception contract describing when DecodeError is raised and what fields it carries.
One-line addition: response_model= decode failures raise httpware.DecodeError (a ClientError subclass), so the standard except httpware.ClientError catches them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pydantic.ValidationError/msgspec.ValidationError/msgspec.DecodeErrorescapedexcept httpware.ClientErrorwhenresponse_model=was set.httpware.DecodeError(ClientError)carryingresponse,model,original(kwargs-only init,__reduce__for pickle parity). Direct child ofClientError, sibling ofStatusError/TransportError/ etc.Client.sendandAsyncClient.sendnow wrap the_decoder.decode(...)call:try: ... except Exception as exc: raise DecodeError(...) from exc._dispatchstays outside the try — transport/status errors are unaffected.ResponseDecoder.decodeprotocol unchanged; docstring grows one sentence documenting the seam wrap.PydanticDecoder/MsgspecDecoderunchanged.Spec:
planning/specs/2026-06-07-decoder-error-design.mdPlan:
planning/plans/2026-06-07-decoder-error-plan.mdBehavior change
Consumers catching
pydantic.ValidationError(ormsgspec.*) directly downstream ofclient.send(..., response_model=...)will no longer match — those exceptions are now wrapped inDecodeError. Switch toexcept httpware.DecodeErroror the broaderexcept httpware.ClientError. The leaked exceptions weren't a documented contract, so no deprecation pass. Target release: 0.8.1.Test Plan
just test— 382 tests pass, 100% coveragejust lint-ci— cleanhttpx2._private API, nofrom __future__ import annotations, noprint()httpware.DecodeErrorresolves;issubclass(DecodeError, ClientError)isTruesendpaths verified throughPydanticDecoder(5 tests intest_client_response_model.py) andMsgspecDecoder(1 seam test intest_decoders_msgspec.py)__reduce__covered (test_decode_error_pickleable)🤖 Generated with Claude Code