Skip to content

Retry Policy

Husnain Ali edited this page Sep 27, 2026 · 3 revisions

Retry Policy

config.retry = .default        // 3 attempts, exponential backoff + full jitter, respects Retry-After
config.retry = .none           // 1 attempt
config.retry = .aggressive     // more attempts, wider backoff

config.retry = RetryPolicy(
    maxAttempts: 4,
    retryableStatusCodes: [408, 429, 500, 502, 503, 504],
    backoff: .exponential(base: 0.5, multiplier: 2, max: 30),
    jitter: .full,
    respectRetryAfter: true,
    maxRetryAfterDelay: 120,
    retryNonIdempotent: false
)
  • Idempotency-aware. GET / HEAD / PUT / DELETE retry by default; POST / PATCH do not, unless retryNonIdempotent: true or the endpoint opts in.
  • Rate limiting. 429 and 503 with a Retry-After header wait exactly that long (capped at maxRetryAfterDelay), overriding the backoff curve. The Retry-After HTTP-date form is parsed with one shared formatter, not one per request.
  • Per-endpoint override:
struct SubmitOrder: Endpoint {
    typealias Response = Order
    var method: HTTPMethod { .post }
    var retryPolicy: RetryPolicy? { RetryPolicy(retryNonIdempotent: true) }   // this POST is safe to repeat
}

Timing goes through a NetworkClock port, so retry logic is unit-tested with TestClock and no real waiting. See Testing and Mocking.

Best practice: do not blindly enable retryNonIdempotent; make each POST opt in only when it is genuinely safe to repeat.

Clone this wiki locally