Retry
Generated clients can retry failed unary operations through
{Service}ClientConfig.RetryStrategy:
var client = new WeatherClient( new Uri("https://api.example.com"), new() { RetryStrategy = new SmithyStandardRetryStrategy(maxAttempts: 3), });null disables runtime retries.
Standard strategy
Section titled “Standard strategy”SmithyStandardRetryStrategy is the recommended strategy. It retries transport
failures, modeled @retryable errors, and transient HTTP status codes (408,
429, 500, 502, 503, 504), using:
- exponential backoff with full jitter and a backoff cap (default base 100 ms, cap 20 s; throttling outcomes back off from a 500 ms base)
- a client-shared retry quota, so a downstream outage cannot amplify traffic through unbounded retries
- the server’s
Retry-Afterheader (seconds or HTTP-date) when present
RetryStrategy = new SmithyStandardRetryStrategy( maxAttempts: 5, maxDelay: TimeSpan.FromSeconds(10));Errors modeled with @retryable implement ISmithyRetryableError, so modeled
retryability works regardless of protocol or status code —
@retryable(throttling: true) errors are treated as throttling.
Simple strategy
Section titled “Simple strategy”SmithySimpleRetryStrategy retries transport failures, HTTP 429, and 5xx
responses, with an optional fixed delay and no backoff, jitter, or quota —
useful in tests:
RetryStrategy = new SmithySimpleRetryStrategy( maxAttempts: 3, delay: TimeSpan.FromMilliseconds(100));Custom strategies
Section titled “Custom strategies”For custom behavior, implement ISmithyRetryStrategy. A strategy is long-lived
and shared by every call on the client; Begin() runs once per operation
execution and returns that execution’s ISmithyRetrySession. The runtime
classifies each failed attempt — deserializing the modeled error when there is
one — and passes the outcome to the session. The outcome carries the attempt
number, the response (null for transport failures), and the exception that
will propagate to the caller if the attempt is not retried.
A stateless strategy can be its own session:
public sealed class BackoffRetryStrategy : ISmithyRetryStrategy, ISmithyRetrySession{ public ISmithyRetrySession Begin() => this;
public SmithyRetryDecision Classify(SmithyRetryOutcome outcome) { if (outcome.Attempt >= 4 || !IsTransient(outcome)) { return SmithyRetryDecision.GiveUp; }
var delay = TimeSpan.FromMilliseconds(100 * Math.Pow(2, outcome.Attempt - 1)); return SmithyRetryDecision.RetryAfter(delay); }
private static bool IsTransient(SmithyRetryOutcome outcome) => outcome.IsTransportFailure || outcome.Response!.StatusCode == HttpStatusCode.TooManyRequests || (int)outcome.Response.StatusCode is >= 500 and <= 599;}A stateful strategy keeps client-wide state (like a retry quota) on the
strategy and per-execution state on the session; the session’s
RecordSuccess hook fires when an attempt succeeds, which is how the standard
strategy refunds quota.
Because the modeled error is deserialized before the retry decision,
outcome.Error lets a strategy retry on specific modeled error types (for
example, a service’s throttling error).
Retries run inside the NSmithy client runtime. Request interceptors run again for each retry attempt, and each attempt starts from the serialized request rather than a previously mutated request.
Streaming request bodies are not retried by the runtime because they cannot be replayed safely in the general case.