Skip to content

Retries

Arun Prakash edited this page Oct 25, 2026 · 1 revision

Retries

Transient failures can be retried automatically with exponential backoff. Off by default.

final client = WordpressClient.forSite(
  siteUrl: Uri.parse('https://example.com'),
  bootstrapper: (b) => b.withRetryPolicy(const RetryPolicy.standard()).build(),
);

RetryPolicy.standard(): up to 3 tries (1 initial + 2 retries), 500 ms base delay doubling each time, jittered, Retry-After honoured.

What is retried

A request is retried when all of these hold:

  • The method is in methods: GET, HEAD, OPTIONS, PUT, DELETE by default. POST and PATCH are never retried unless you add them, so a create cannot be sent twice by accident.
  • The failure is transient: a timeout or connection error (retryOnNetworkError), or a status in statusCodes (429, 502, 503, 504).
  • The body is replayable. Multipart uploads and streamed bodies are not retried.
  • Fewer than maxAttempts tries have been made.

Cancelled requests are never retried, and cancelling during the wait stops it immediately.

Options

const RetryPolicy(
  maxAttempts: 4,                                // total tries, including the first
  baseDelay: Duration(seconds: 1),               // delay before retry 1; doubles each time
  maxDelay: Duration(seconds: 20),               // cap for the computed delay
  jitter: true,                                  // wait 50-100% of the computed delay
  statusCodes: {429, 503},
  methods: {HttpMethod.get, HttpMethod.head},
  retryOnNetworkError: true,
  honorRetryAfter: true,                         // use the Retry-After header when present
  maxRetryAfter: Duration(seconds: 30),          // longer Retry-After: give up and return the response
)

With Retry-After present the server's delay is used as is; otherwise the delay is baseDelay * 2^(retry-1), capped by maxDelay.

Per request

Only WordpressRequest (raw requests, custom interfaces) carries a policy; it overrides the client's.

await client.raw(WordpressRequest(
  method: HttpMethod.get,
  url: RequestUrl.relative('posts'),
  retryPolicy: RetryPolicy.none,                 // never retry this one
));

How many tries were needed

final raw = await client.raw(request);
print(raw.extra[WordpressRequest.metaAttempts]);   // 1 when it worked first time

duration on the response covers all tries and waits.

With other features

  • Auth: a 401 on a requireAuth request triggers one re-authentication and a retried request, independent of the policy.
  • Cache: only the network call is retried; a cache hit never reaches the retry loop.
  • GraphQL: queries sent with useGet: true are retried (GET); regular POST queries are not. See GraphQL.
  • Tests: set sleep: (_) async {} to skip waiting. See Testing.

Dio interceptors

If you also add a retry interceptor with withDioInterceptor, retries multiply. Use one mechanism.

Clone this wiki locally