Skip to content

Commit 2dfa367

Browse files
lesnik512claude
andcommitted
docs(errors): document CircuitOpenError; fix stale composition intro
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 294030f commit 2dfa367

2 files changed

Lines changed: 6 additions & 2 deletions

File tree

docs/errors.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
`httpware` raises typed exceptions automatically — everything inherits `ClientError`, and HTTP responses with 4xx/5xx status raise status-keyed `StatusError` subclasses without you having to call `response.raise_for_status()`.
44

5-
For the resilience-specific errors (`RetryBudgetExhaustedError`, `BulkheadFullError`) see the [Resilience reference](resilience.md).
5+
For the resilience-specific errors (`RetryBudgetExhaustedError`, `BulkheadFullError`, `CircuitOpenError`) see the [Resilience reference](resilience.md).
66

77
The status-keyed exception tree is shared between `Client` and `AsyncClient`. Catching `NotFoundError` in sync code uses the same import as catching it in async code (`from httpware import NotFoundError`).
88

@@ -27,6 +27,7 @@ ClientError (catch-all for anything httpware raises)
2727
│ └── ServiceUnavailableError (503)
2828
├── RetryBudgetExhaustedError (a retry was needed but the budget refused)
2929
├── BulkheadFullError (acquire_timeout elapsed before a slot opened)
30+
├── CircuitOpenError (circuit is OPEN or HALF_OPEN probe slot taken; request not forwarded)
3031
├── DecodeError (response_model= decoder failed; HTTP call itself succeeded)
3132
└── MissingDecoderError (no registered decoder claims response_model=; fires before the HTTP call)
3233
```
@@ -119,6 +120,9 @@ exc.response.request.method # the HTTP method
119120
- `max_concurrent: int` — the configured cap
120121
- `acquire_timeout: float | None` — the configured timeout
121122

123+
`CircuitOpenError` carries:
124+
- `retry_after: float | None` — seconds until the circuit will next admit a probe; `None` when a concurrent probe is already in flight (HALF_OPEN slot taken).
125+
122126
Use these for caller-side logging / alerting:
123127

124128
```python

docs/resilience.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- **`RetryBudget`** — Finagle-style token bucket; safe to share across sync `Client` and `AsyncClient` in the same process. (Finagle-style bounds the global retry rate to prevent retry storms when downstreams degrade.)
77
- **`Bulkhead` / `AsyncBulkhead`** — concurrency limiter with bounded acquire-wait (`threading.Semaphore` and `asyncio.Semaphore` respectively)
88

9-
The canonical composition is `middleware=[AsyncBulkhead(...), AsyncRetry()]``AsyncBulkhead` outside `AsyncRetry` so one slot covers all retry attempts of a single call. Reach for the [Middleware guide](middleware.md) when you want to write your own resilience policy.
9+
A key ordering constraint: `AsyncBulkhead` must sit inside `AsyncRetry` so one slot covers all retry attempts of a single call. For the full recommended ordering across all four primitives, see [Composition](#composition). Reach for the [Middleware guide](middleware.md) when you want to write your own resilience policy.
1010

1111
## `AsyncRetry`
1212

0 commit comments

Comments
 (0)