Retries and timeouts
Every generated client retries failed requests with exponential backoff, and limits how long each attempt can take. You don't have to configure any of it: these settings are for when the defaults don't fit your API.
Settings
Set these project-wide under Reliability in SDK Studio, or on one endpoint when a method needs different treatment. Configure nothing and every language ships with the defaults below. The generated README states the policy that package actually ships with.
Max attemptsnumberTotal tries, including the first, from 1 to 10.
reliability.maxAttempts
Base backoffmillisecondsThe first retry's delay, before jitter. Up to 120,000, which is 2 minutes.
reliability.backoffMs
Max backoffmillisecondsThe ceiling on any single delay. Up to 600,000, which is 10 minutes.
reliability.maxBackoffMs
Retry status codeslist of status codesWhich HTTP statuses can trigger a retry: any from 100 to 599. See what gets retried for the rule on POST.
reliability.retryOn
TimeoutmillisecondsHow long one attempt can take. Up to 600,000, which is 10 minutes.
reliability.timeoutMs
Leave a field blank and the client keeps its built-in default. A value that can't work is dropped rather than shipped, so a blank field and a 0 timeout both leave the layer beneath in charge.
1 disables retries. 3 means the original request plus two retries.
Precedence
Each layer overrides the one before it, field by field. An endpoint that sets only Timeout keeps your project's Max attempts.
- The built-in defaults
The policy in the table above.
- Your project default
Set in SDK Studio. Applies to every method.
- A per-endpoint override
Set in SDK Studio. Applies to one method.
- The developer's client config
Set when they create the client.
- The developer's per-call option
Set on a single call.
What developers can change
Whatever you ship is a default. A developer using your SDK can override it for their client or for one call:
const client = new Acme({
auth: { bearer: process.env.API_TOKEN! },
timeoutMs: 10_000,
retry: { maxAttempts: 5 },
});
const user = await client.users.get(
{ id: "usr_1" },
{ timeoutMs: 2_000, retry: { maxAttempts: 1 } },
);What gets retried
Being on the Retry status codes list isn't always enough. The method matters too.
| Request | Failed with a listed status | Failed with a network error or timeout |
|---|---|---|
GET, HEAD, PUT, DELETE, OPTIONS | Retried | Retried |
POST, PATCH | Only on 429 and 503 | Only if your spec declares an idempotency header on the operation. See idempotency |
A 500 from a POST may mean the write happened. The clients don't retry it, even if you add 500 to the list, because a 429 or 503 is the only status that says plainly the request wasn't processed.
Backoff
The delay doubles with each attempt, starting from Base backoff, with up to one Base backoff of random jitter added. That way, clients that fail together don't all retry in step and overload a recovering service. Max backoff caps the result.
If your API sends a Retry-After header, the client uses that instead, as a number of seconds or a date. It's still capped by Max backoff.
With a Max backoff of 8 seconds, a server asking for 60 seconds gets 8, and the retry arrives too soon. If you rate-limit by the minute, raise the ceiling.
Timeouts
Timeout limits one attempt, not the whole call. Three attempts at 30 seconds can take over 90 seconds against a dead server, plus the backoff between them.
A 0 doesn't disable the timeout. It's ignored, and the default applies. For a long-running call, set a long timeout on that endpoint.
Errors after retries run out
When the attempts are used up, the caller gets a typed error. These names are from TypeScript, and most languages match them closely.
| Error | Meaning |
|---|---|
SdkTimeoutError | An attempt ran past its timeout |
SdkNetworkError | DNS, connection, TLS or socket failure |
SdkRateLimitError | 429 |
SdkInternalServerError | Any 5xx |
SdkHttpError | Any other non-2xx response. It's the parent of the status errors |
HTTP errors carry the status code, body, headers and, when your server sends one, the request ID. Go uses errors.Is(err, ErrRateLimit) against sentinels rather than subclasses.
try {
await client.users.get({ id: "usr_1" });
} catch (error) {
if (error instanceof SdkNotFoundError) {
return null;
}
if (error instanceof SdkHttpError) {
console.error(error.statusCode, error.requestId);
}
throw error;
}Log the request ID when you can. It's the quickest way to match a failed call with your server's traces.
From the CLI
octri sdk settings set reliability.maxAttempts 5 changes one value. The keys are in the settings reference, and the command in SDK commands.