Skip to content

Latest commit

 

History

History
27 lines (16 loc) · 3.48 KB

File metadata and controls

27 lines (16 loc) · 3.48 KB

Errors

StatusError and all its 4xx/5xx subclasses are constructed with a single positional response: httpx2.Response. Subclasses do not override __init__. All fields are available via exc.response.* (status code, headers, content, request, etc.).

raise NotFoundError(response)          # correct
exc.response.status_code               # 404
exc.response.request.url               # URL of the failed request

__repr__ and the str() summary redact URL userinfo (user:pass@) and mask the values of known-sensitive query and fragment parameters (e.g. token, api_key, secret) to avoid leaking credentials in tracebacks.

The error-mapping table (what httpx2 exception maps to which httpware exception) lives in src/httpware/_internal/exception_mapping.py and fires at the terminal in src/httpware/client.py. Status-keyed exceptions are looked up via the STATUS_TO_EXCEPTION table in src/httpware/errors.py. Unknown 4xx falls back to ClientStatusError; unknown 5xx falls back to ServerStatusError.

TimeoutError inherits from both httpware.ClientError and builtins.TimeoutError so except builtins.TimeoutError (the form asyncio.wait_for uses) also catches httpware-raised timeouts.

DecodeError covers the case where response_model= is set, the HTTP call itself succeeded, but the active ResponseDecoder raised. The wrap happens in _BoundDecoder.decode (decoders/_resolver.py) — except Exception translates any decoder-side failure into DecodeError(response=..., model=..., original=...) with raise ... from exc chaining. The original attribute exposes the underlying library exception (e.g., pydantic.ValidationError, msgspec.ValidationError); __cause__ carries the same reference.

The "no __init__ override" rule scopes only to StatusError subclasses. Non-status ClientError subclasses — DecodeError, MissingDecoderError, BulkheadFullError, RetryBudgetExhaustedError, CircuitOpenError, ResponseTooLargeError — deliberately define __init__ with keyword-only fields.

These six non-status ClientError subclasses inherit __reduce__ from _KeywordReduceMixin, which pickles via self.__dict__ and reconstructs via cls(**kwargs). This requires self.__dict__ to exactly mirror the __init__ keyword parameters: an attribute stored beyond those parameters raises TypeError on unpickle (unexpected keyword argument), and a keyword parameter __init__ doesn't assign to self is silently dropped if it has a default (unpickle reverts to it) or raises TypeError if it doesn't.

ResponseTooLargeError is a non-status ClientError; it does not carry a StatusError-style positional response and is not in STATUS_TO_EXCEPTION. Because it is neither a StatusError, NetworkError, nor TimeoutError, it is not retried and does not count toward the circuit breaker. The cap mechanism itself — when it fires, the two enforcement sites, and the declared/streamed reason split — is documented in client.md; the full field-by-field API is in docs/errors.md.

Security: request headers are reachable via exc.response.request

StatusError holds the raw httpx2.Response. Request headers — including Authorization, Cookie, and Proxy-Authorization — remain reachable at exc.response.request.headers. httpware masks URL userinfo and known-sensitive query/fragment values in messages and repr, but does not strip headers. Handler authors must redact before logging or serializing a caught error.