Overview
Requests to a model can fail for reasons that have nothing to do with your code, at the inference step itself: a provider is briefly overloaded, a connection drops, a stream is cut off halfway through an answer. Gloo AI already absorbs much of this for you with provider failover. What reaches your application is a smaller set of errors, and every one of them tells you what to do next. In this tutorial you’ll build a small client wrapper that reads those signals and acts on them: retry when the API says a retry can work, wait as long as the API asks, stop when retrying would only fail again, and keep a streamed answer on screen when the stream breaks.What You’ll Build
By the end of this tutorial you’ll have a client module with:- An error classifier that turns any Gloo AI error response into one of four decisions: success, retry, fix the request, or stop
- Backoff with jitter that honors
Retry-Afterand gives up within a fixed budget - A request wrapper that retries non-streaming calls and logs what support needs
- Failover awareness: what to expect in a response that was served after a provider failed, and what not to build yourself
- Stream recovery that retries before any text is shown and continues the answer once after text is shown
Prerequisites
Before starting, ensure you have:- A Gloo AI Studio account
- Your API key from the API Keys page, exported as
GLOO_API_KEY - Authentication setup: complete the Authentication Tutorial first
- Python 3.10+ with
requests, or Node.js 18+ (the TypeScript examples use the built-infetch) - Familiarity with streaming. If you haven’t streamed a completion before, start with Streaming AI Responses in Real Time.
This tutorial is the hands-on build. For the policy behind each choice, read the best practices alongside it: Error Handling and Retries and Handling Streaming Failures.
Step 1: Learn What an Error Tells You
Every Gloo AI error carries a structurederror object. Branch on its fields, never on the wording of the message:
The direct endpoints add a few more signals:
Some refusals use a
detail field instead of an error object. A content-policy refusal carries a detail object with a string code and retryable: false; an authentication failure on the guarded endpoints carries detail as a plain string. Your classifier needs to handle all three shapes.
The errors you’ll see most fall into four groups:
Two cases catch people out:
3001 / INTERNAL_ERRORis never retryable, while a mid-stream2014 / PROVIDER_STREAM_ERRORis. Same family of status codes, opposite decisions. That’s why the classifier readsretryable, not the status.3007 / GATEWAY_DEADLINEmeans a non-streaming request ran past Gloo’s response deadline. Sending it again meets the same deadline. Stream long generations, or lower the output limit or the reasoning effort.
429 variants are explained in Rate Limits.
Step 2: Classify Every Response
The classifier is the heart of the client. It reads the status, headers and body, and returns one decision. It is fail-closed: only an explicitretryable: true leads to a retry. A missing field, an unparseable body or an unknown error never does.
- Returns
successfor any2xx, keepingX-Request-Id - Reads the
errorobject first, then adetailobject, so both error shapes work. A plain-stringdetail(an authentication failure) has noretryablefield, so it is never retried - Prefers the
X-Gloo-Error-Codeheader for the code on the direct endpoints, thenerror.name - Marks
400,404,405,413and422as fix the request, and everything else that isn’t retryable as stop - Takes the wait from the
Retry-Afterheader, or fromerror.retry_afterin the body - Falls back to a safe, generic message for users when the body has none
Step 3: Back Off With Jitter
When a retry is allowed, how long you wait matters as much as whether you retry. Two rules:- When the response has
Retry-After, wait at least that long. The server knows when capacity returns. - Otherwise, back off exponentially with jitter. Doubling the wait gives a struggling provider room to recover; jitter spreads out clients that failed at the same moment, so they don’t all retry in the same instant.
backoff_delay is for background work, streaming or not: 1, 2, 4 and 8 seconds plus up to a second of jitter, never more than 30 seconds. full_jitter_delay is for when someone is waiting, on an interactive request or a live stream; it picks a random wait of at most 2 seconds.
Retry budgets
Always bound the number of attempts. These are the budgets used in this tutorial, from the retry rule and Handling Streaming Failures:Step 4: Wrap Your Requests
Now put the classifier and the backoff together in apost method. It sends the request, classifies the response, and either returns, waits and retries, or raises. A background flag picks the budget: by default it assumes someone is waiting for the answer.
- Picks the budget from
background, using the budgets in the table above - Retries a network failure with no response (connection refused, reset, client timeout) with backoff, because there is no error object to read
- Retries a
retryable: trueerror, waiting forRetry-Afterplus a little jitter, or backing off - Raises immediately for fix the request and stop, so your code can handle them
- Raises straight away when the server asks for a longer wait than the budget allows, so the caller can tell the user to try again shortly or reschedule the job
- Logs
code,retryable,trace_idandX-Request-Idon every failed attempt - Warns when the response was served by a replacement for a deprecated model (
X-Gloo-Model-Redirected-From)
3003 / SERVICE_UNAVAILABLE asks for 30 seconds, this request raises at once and your user sees the message instead of a frozen screen. For work nobody is watching, such as a nightly batch, pass background=True ({ background: true } in TypeScript) to get the larger budget:
Step 5: Work With Provider Failover
Most models in the Gloo AI catalog are served by more than one provider. When the provider serving your request fails, Gloo AI retries it on another provider of the same model, on every chat and responses endpoint, with no configuration. Your client doesn’t need to switch models or add a second provider to ride out an outage. What changes is what a response looks like after failover:
When an error does reach you, it means one of two things:
- No provider could take the request. You get
503with2002 / PROVIDER_UNAVAILABLEandretryable: true. Retry with backoff: a later request benefits from Gloo having already routed around the failing provider. If it persists, try a different model. - The failure would happen on every provider. An invalid request, an authorization problem, a content-policy block or a billing limit is returned straight away instead of being repeated elsewhere. Your classifier already marks these as fix the request or stop.
- Anthropic Messages is served by Anthropic only, with no failover. A
529there means retry afterretry-after. The official Anthropic SDKs already do this, so use their built-in retries rather than this wrapper. - Embeddings returns
503withretryable: truewhen the provider fails. The classifier handles it.
Failover works within a time budget for each request, so a request never waits indefinitely. For long generations, stream rather than raising your client timeout: a non-streaming request that runs past Gloo’s response deadline gets
3007 / GATEWAY_DEADLINE, which isn’t retryable unchanged.Step 6: Recover Interrupted Streams
Streaming adds one complication: once a stream has started, the HTTP status is already200, so later failures arrive inside the stream. A streamed request can end four ways:
The other question is whether the user has already seen text:
- Nothing shown yet: a full retry is invisible to the user. Retry up to the budget.
- Text on screen: a full retry would erase or duplicate it. Keep the text, and send one Continue request that asks the model to pick up where it stopped.
- Classifies a non-
200response with the sameclassifyfunction, so a401before the stream is never retried - Collects text as it arrives and records whether any was shown, and whether a tool call was emitted
- Ends on
stop,lengthortool_callsas complete, and oncontent_filteras final - Stops on
retryable: false, keeping any partial text - Treats a dropped connection, a client timeout or a malformed event as a transport error, which is plausibly transient
- Retries before any text is shown: 2 full retries with short waits for live streams; 4 retries (5 requests in total) with
min(2^(attempt-1), 30)seconds plus jitter for background jobs - Honors a server-requested wait when it fits the budget, and fails fast when it doesn’t (for example a 30-second
Retry-Afteron a live stream) - After text is shown, sends one Continue request, using a new user message that carries the partial answer
- Skips the automatic Continue when a tool call was emitted, so a side effect can’t run twice
Designing the interrupted state
Whenstatus is interrupted, the partial answer is still in result.text and still on screen. Keep it visible and mark it as incomplete. Offer Continue as the main action (it appends) and Try again as a secondary action that’s clearly labelled as replacing the answer. A user-initiated Continue doesn’t count against the automatic budget.
Direct Responses streams
On Direct Responses, a stream that fails after output has started ends with aresponse.failed event instead of an error chunk. response.error carries the string code (the same value as X-Gloo-Error-Code), gloo_code, retryable and, when it applies, retry_after. The same decisions apply: read retryable, check whether text was shown, and continue or surface.
Step 7: Test the Failure Paths
Don’t wait for a real outage to test your retry logic. Both clients take their transport and their sleep function as constructor arguments, so you can script failures and check the decisions without calling the API or waiting.400 content-policy refusal, a network failure with no response, a stream that drops after the first chunk, and a stream that ends with content_filter.
Step 8: Log What Support Needs
When something fails repeatedly, the IDs in your logs are what let support find the exact request. For every failed attempt, record:error.code,error.name,error.faultanderror.retryabletrace_idfrom the error, andX-Request-Idon the direct endpoints (stream errors on the direct endpoints carry notrace_id, soX-Request-Idis the one to keep)developer_message, when present- The model, the attempt number and the wait before the retry
- For streams: whether text was shown, its length, whether a Continue was sent, and whether a tool call was emitted
trace_id.
Troubleshooting
Every error is retried, including401 and 400
: Check that retries depend on retryable === true and nothing else. A classifier that retries on “any 5xx” or “any error without retryable: false” is fail-open and will loop on errors that can’t succeed.
Requests hang for 30 seconds before failing
: A 3003 / SERVICE_UNAVAILABLE asked the client to wait for Retry-After, and the request was sent with background=True. For requests someone is waiting on, keep the default interactive budget: the client raises straight away and your UI can show “please try again shortly”.
A 429 is never retried
: Not every 429 is retryable. A provider rate limit (2004 / RATE_LIMIT) is; a billing or account limit is not, and comes back with retryable: false. See Rate Limits.
408 or 504 with 3007 / GATEWAY_DEADLINE on long answers
: The non-streaming request ran past Gloo’s response deadline. Use GlooStreamClient, or lower max_tokens or the reasoning effort.
A reasoning model returns no text with finish_reason: "length"
: That isn’t an outage, and it isn’t retried. The model spent the output limit thinking. Raise max_tokens or lower the reasoning effort; see Set your output limit for reasoning models.
A streamed answer appears twice after a failure
: The client did a full retry after text was already shown. Track whether text was shown, and use Continue, not a full retry, once it has.
The continued answer repeats the last sentence
: Continuation is best effort. De-duplicate the overlap where the two parts meet before you render the second part.
Next Steps
Error Handling and Retries
Retry policy, budgets and backoff across every endpoint.
Handling Streaming Failures
The full decision tree and reference implementations for streams.
Errors Reference
Every error code, the error object, and streaming error delivery.
Provider Failover
When Gloo AI retries on another provider, and when it doesn’t.
Rate Limits
The different kinds of
429, and which ones to retry.Prompt Caching
Cut cost and latency on the requests your client sends.

