Integration Error Handling
Tell the difference between error types and respond to each in the right way.
What you'll learn
- Classify errors as client, server or network faults
- Map HTTP status codes to appropriate responses
- Distinguish retryable from non-retryable failures
- Surface actionable error information
- Fail safely without losing work
6 min
Errors are not all alike
The single biggest mistake in integration code is treating every failure the same way — catch, log, retry, repeat. Different errors demand different responses, and conflating them either hides real bugs or hammers a service that is telling you to stop.
Errors fall into three broad families: client errors (your request is wrong), server errors (the other side is broken), and network errors (the request did not complete cleanly). Each calls for different handling. The first job of good error handling is to classify the failure correctly, because every sensible response flows from knowing which kind of error you are looking at.
Reading status codes
HTTP status codes are your primary signal. Broadly:
- 2xx — success.
- 4xx — client error: your fault. Fix the request; retrying unchanged will not help.
- 429 — a special 4xx meaning slow down, which is retryable after a wait.
- 5xx — server error: their fault, often transient and worth retrying with backoff.
A 400 or 422 means your payload is malformed — log it loudly so the bug gets fixed. A 401 or 403 is an auth problem. Treating a 400 as retryable just buries a defect under pointless retries. Read the body too, as it usually carries a more specific reason.
Retryable or not
The retryable distinction is the one that matters most operationally. Get it wrong in one direction and you give up on failures that would have recovered; get it wrong in the other and you retry into a wall.
Retry the transient and the temporary: timeouts, connection failures, 429, and most 5xx. Do not retry the deterministic: malformed requests, validation failures, and auth errors, which will fail identically every time. And before retrying any write, ensure it is safe to repeat — see the idempotent consumer. A network error is the tricky case, because you may not know whether the work happened; idempotency resolves the ambiguity.
Surface and fail safely
When an error is final, handle it well. Two things matter: making the failure actionable, and failing without losing work.
try:
result = client.call(request)
except ValidationError as e:
log.error('bad request', detail=e.body)
raise
except TransientError:
enqueue_for_retry(request)Actionable means capturing enough context — what was attempted, the status, the response body — to diagnose without guesswork; see logging API calls. Failing safely means not dropping data on the floor: queue the work, return a clear error to the caller, and leave the system in a recoverable state rather than a half-finished one.
Key takeaways
- Classify failures as client, server or network before reacting
- 4xx is usually your fault and not retryable; 5xx and 429 often are
- Retry transient errors with backoff; never retry deterministic ones
- Resolve network-error ambiguity with idempotency before retrying writes
- Capture actionable context and fail without losing work
FAQ
Which errors should I retry?
Transient ones: timeouts, connection failures, 429 rate limits and most 5xx server errors. Do not retry 4xx client errors like malformed requests or auth failures, since they will fail identically every time.
What does a 4xx error mean?
It means the request itself is the problem — malformed, unauthorised, or invalid. The fix is to correct the request, not to retry it. Log 4xx errors prominently so the underlying defect actually gets addressed.
How do I handle a network error where I do not know if it worked?
Treat the outcome as unknown. Make the operation idempotent so a retry cannot duplicate its effect, then retry safely. Idempotency is what resolves the ambiguity of a request that may or may not have completed.
Ready to build?
Read the API reference, grab the OpenAPI spec, and ship a resilient integration.