Skip to content

Commit a38ace1

Browse files
lesnik512claude
andcommitted
docs(release): draft 0.8.1 notes (DecodeError)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7e1ef35 commit a38ace1

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

planning/releases/0.8.1.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# httpware 0.8.1 — `DecodeError` closes the decoder-exception gap
2+
3+
**Patch release with one behavior change.** Code that catches `httpware.ClientError` (the advertised catch-all) now actually catches every failure mode of `response_model=` decoding. Code that catches `pydantic.ValidationError` or `msgspec.*` *directly* downstream of `client.send(..., response_model=...)` will no longer match — those exceptions are now wrapped.
4+
5+
## The gap
6+
7+
Before 0.8.1, when `response_model=` was set, `Client.send` and `AsyncClient.send` invoked the active `ResponseDecoder` without a translation step. Whatever the decoder raised — `pydantic.ValidationError` (schema mismatch or malformed JSON via `TypeAdapter.validate_json`), `msgspec.ValidationError`, `msgspec.DecodeError`, or anything else — escaped untranslated. `except httpware.ClientError` did not catch it. Consumers either had to import the decoder library at the call site or skip the decoder entirely and decode the raw `httpx2.Response` by hand.
8+
9+
## The fix
10+
11+
New `httpware.DecodeError(ClientError)` — direct child of `ClientError`, sibling of `StatusError` / `TransportError` / `RetryBudgetExhaustedError` / `BulkheadFullError`. Both `Client.send` and `AsyncClient.send` now wrap the decoder call:
12+
13+
```python
14+
try:
15+
return self._decoder.decode(response.content, response_model)
16+
except Exception as exc:
17+
raise DecodeError(response=response, model=response_model, original=exc) from exc
18+
```
19+
20+
The middleware/`_dispatch` call stays outside the try — transport and status errors are unaffected. Decoder implementers do not need to import or raise `DecodeError`; the seam translates whatever they raise.
21+
22+
Fields on `DecodeError`:
23+
24+
- `response: httpx2.Response` — the response whose body failed to decode (status, headers, request URL all available).
25+
- `model: type` — the type that was passed as `response_model=`.
26+
- `original: BaseException` — the underlying library exception. Also available via `__cause__`.
27+
28+
```python
29+
from httpware import AsyncClient, ClientError, DecodeError
30+
31+
32+
try:
33+
user = await client.get("/users/1", response_model=User)
34+
except DecodeError as exc:
35+
_LOGGER.error(
36+
"decode failed for %s into %s: %s",
37+
exc.response.request.url,
38+
exc.model.__name__,
39+
exc.original,
40+
)
41+
raise
42+
except ClientError:
43+
raise
44+
```
45+
46+
## Migration
47+
48+
If you catch `pydantic.ValidationError` or `msgspec.*` directly downstream of `client.send(..., response_model=...)`, switch to `except httpware.DecodeError` (or the broader `except httpware.ClientError`). The previously-leaking exceptions weren't a documented contract, so there's no deprecation pass. The fix is the fix.
49+
50+
If you already catch `httpware.ClientError`, nothing changes — your handler now also covers the decode-failure path it should have covered all along.
51+
52+
## Touched surface
53+
54+
- `httpware.DecodeError` — new public class, re-exported from the top level.
55+
- `Client.send` / `AsyncClient.send` — both wrap the decoder call (one `try/except` each).
56+
- `ResponseDecoder.decode` — protocol signature unchanged; docstring grows one sentence documenting the seam wrap.
57+
- `PydanticDecoder` and `MsgspecDecoder` — unchanged.
58+
- Docs: `docs/errors.md` (hierarchy + new section), `planning/engineering.md` (Seam B contract + §4 paragraph), `README.md` (one-line note on the `response_model=` paragraph).
59+
60+
## See also
61+
62+
- [`planning/specs/2026-06-07-decoder-error-design.md`](../specs/2026-06-07-decoder-error-design.md) — design rationale.
63+
- [`planning/plans/2026-06-07-decoder-error-plan.md`](../plans/2026-06-07-decoder-error-plan.md) — implementation plan.
64+
- PR [#32](https://github.com/modern-python/httpware/pull/32).

0 commit comments

Comments
 (0)