Skip to content

Results, batching and retries

Inspect a result

result = await http.get("/items")
if result.ok:
payload = result.unwrap().payload
else:
status = result.status
error = result.error
body = result.response.content if result.response is not None else None
ValueMeaning
result.completedExecution, body read and decoding completed
result.okCompleted and terminal HTTP status is 2xx
result.statusTerminal response status, or None when no terminal response arrived
result.errorRecorded exception, if one exists
result.attemptsRunner attempts, not redirect/auth exchange count
result.elapsedExecution elapsed duration
result.responseTerminal attempt’s terminal response evidence
result.historyFollowed redirect/auth response heads in the terminal attempt
result.executionExisting common-execution report, including retry evidence

A completed 404 remains inspectable but is not ok. A connection failure has no fabricated status. Size-limit and read failures retain any collected bytes with body_complete=False. Decode failures retain complete bytes with decoded_as=None. JSON null is a successfully decoded None; empty content is separately labelled decoded_as="empty". A complete 206 response does not imply the complete resource.

unwrap() re-raises recorded exceptions. For unsuccessful outcomes without an exception, it raises HttpResultError carrying .result. Routine result repr omits payloads; explicit body/header/error access can reveal sensitive data.

Batch once

async with HttpClient.create(base_url="https://provider.example/", max_concurrency=8) as http:
messages = [http.build_request(f"/items/{number}") for number in range(4)]
batch = await http.batch(messages)
for message, result in zip(messages, batch.results, strict=True):
if result.ok:
consume(result.unwrap().payload)

The batch retains input order, including failures. It uses one execution runner over the underlying attempt method. It does not run execution-managed send inside another execution batch. A single call likewise has one runner. Call configuration applies to the whole batch; use separate calls for different policies.

Concurrency is bounded within a batch, not across independently started calls or across containers. fail_fast follows execution failures and batch boundaries; a successfully returned HTTP 404 alone is not an execution failure. Skipped items retain their positions according to the execution result contract.

Opt into retries

import httpx
from common_execution import RetryPolicy
from common_http import HttpClient
policy = RetryPolicy.http(
max_retries=2,
extra_exceptions=(httpx.ConnectError, httpx.ReadTimeout),
delay=0.25,
max_delay=5,
)
async with HttpClient.create(base_url="https://provider.example/", retry=policy) as http:
result = await http.get("/items")

The default makes no retries. RetryPolicy.http selects 429/500/502/503/504 by default and accepts a different statuses set. Select native transport exceptions explicitly. Server Retry-After hints are bounded by the configured maximum delay. The compatible execution release must include HTTP-date and typed-header support.

The policy authorizes repeating the operation, including writes. Decide whether a write is repeatable and whether its provider supports an idempotency key. An error does not prove the remote server did not act. HTTP does not invent idempotency keys or turn all errors into retries. Cancellation is resource cleanup, not a replay.

Pacing and time budgets

from common_execution import RateLimit
call = http.call_config(rate_limit=RateLimit.per_second(5), timeout=60)

The inherited limiter paces task starts within that run. It is not a client-global or distributed quota, and does not charge every redirect/auth exchange. Supply admit= for admission before each physical exchange; see integration boundaries.

timeout is per attempt. Network timeouts apply to phases/chunk waits. For a whole logical operation deadline including retry waits:

import asyncio
async with asyncio.timeout(120):
result = await http.get("/items")

Close a client only after its operations finish or are cancelled and awaited. Active-operation checks include retry delays and complete batches.