Appearance
Error Handling & Retries
The gateway is the single place that decides what to do when one of our resources fails. Consumers see one error contract; our resources can fail however they want.
Error Categories
Every error falls into one of four buckets. The retry policy is determined by the bucket, not by the specific error code.
| Category | Examples | Retry? |
|---|---|---|
| Transient | Network timeout, 5xx, 429 | Yes, with backoff |
| Permanent | 400, 401, 403, 404, schema mismatch | No — surface to consumer |
| Quota | SF governor limit hit | Yes, after backoff window |
| Unknown | Anything not classified | Yes, once, with backoff |
Retry Strategy
Bounded
- Max attempts: 3 (5 for idempotent writes with a key)
- Max total time: 30s
- Backoff: exponential with jitter, starting at 500ms
Idempotent
Only idempotent requests are retried. Non-idempotent requests fail fast on the first error. This is why the Idempotency-Key header is required on writes.
Circuit Breaker
If a resource returns errors above a threshold (e.g., >50% errors over 1 minute), the circuit opens. Subsequent calls fail fast with a circuit_open error for a cool-down period, then half-open probes.
Error Response Shape
Every error from the gateway uses this shape, regardless of which resource failed:
json
{
"error": {
"code": "sf_quota_exceeded",
"message": "Salesforce API limit reached for this user",
"retryable": true,
"vendor_code": "REQUEST_LIMIT_EXCEEDED",
"request_id": "uuid"
}
}Consumers branch on retryable and code. Resource-specific details are in vendor_code for debugging.
Mapping Resource Errors to Categories
Each connector has an explicit mapping table. For Salesforce:
| SF Error | Category |
|---|---|
REQUEST_LIMIT_EXCEEDED | Quota |
INVALID_SESSION_ID | Transient (refresh token, retry once) |
MALFORMED_QUERY | Permanent |
STORAGE_LIMIT_EXCEEDED | Permanent |
| Network timeout | Transient |
| 5xx | Transient |
The mapping lives in code, not in prompt logic. It is unit-tested.
Logging
Every error logs:
request_idresourceendpointerror_codevendor_codelatency_msattempt(1, 2, 3)trace_idconsumer_id
See Observability.