Errors, retries and safe rendering
Real calls fail and real output is untrusted. Handle both before you ship.
Words on this page (6)
- backoff
- : Waiting longer after each failed attempt before retrying, for example 1, 2, then 4 seconds.
- jitter
- : A little randomness added to retry waits so many clients do not retry at the same moment.
- logarithm
- : The power you raise a base to in order to get a number. log base 2 of 8 is 3 because 2 to the power 3 is 8.
- API
- : Application programming interface: a way for one program to call another, usually over HTTP.
- git
- : The standard tool for tracking changes to code.
- prompt
- : The input text you give a model, including instructions, examples and data.
Errors
Read the status code. 400: your request is malformed or too long, so retrying will not help. 401 or 403: the key is wrong or lacks access. 429: rate limit, so wait and retry with (1 second, 2, 4, with some random ). 500 or 529: the provider is overloaded, so retry a few times and then show a clear message. Set timeouts. the request id from the response headers, which providers ask for in support tickets. Show users a plain message, never the raw error.
Keys stay on the server
An key in browser code is a key anyone can copy. Call the provider from your backend, keep the key in an environment variable and rate-limit per user. Never commit keys to .
Rendering model output safely
Models often reply in markdown. If you convert it to HTML and insert it into the page, a prompt-injected or odd answer can contain a script tag or a link that runs code. Sanitize the HTML with a library, or render markdown with a safe renderer that does not allow raw HTML. Treat the output like user input, because it can be shaped by user input. Do the same before putting model text into a database query or a shell command.
How retry with backoff works
- Send the request. It fails with a retryable error such as 429 (rate limited) or an overloaded or 5xx server error.
- Wait about 1 second plus a small random amount, then try again.
- If it fails again, wait about 2 seconds, then about 4.
- Stop after a fixed number of tries and show the user a plain message.
Without jitter, the waits total 1 + 2 + 4 = 7 seconds across the three retries. Jitter adds a random fraction so many clients do not retry together. Always cap the number of attempts and the longest wait.
A second example: retrying makes it worse
A request fails with 400 because the is too long. Retrying five times sends the same oversized prompt five times and fails five times. The fix is to shorten the prompt. A 401 behaves the same way: the key is wrong, and repeating the call changes nothing. Retry only errors that can pass on their own: rate limits, overload and timeouts.
What goes wrong when you render output
Markdown converted to HTML with raw HTML allowed can carry a script tag. Symptom: odd pop-ups or a changed page layout after one particular answer. To confirm, test your renderer with a harmless string such as an image tag with a broken source and an alert handler. If the alert fires, the renderer is unsafe.
Links can use a "javascript:" address that runs code when clicked. A safe renderer blocks that address type.
Model text placed into a database query can change the query. Pass values separately from the query text (a parameterized query) so the database treats them as data.
Try it yourself
Write a function that returns the wait before each of 5 attempts. Without jitter it should return 1, 2, 4, 8, 16 seconds. Add jitter and run it three times to see the lists differ. Then feed your page the harmless test string above and check that nothing runs.
Common mistake
Showing raw error text to users. It can reveal internal details and it is unhelpful.
A chatbot answers with a friendly message that contains a hidden script tag, planted earlier by a user's clever prompt. The page inserts the reply as HTML, and the script runs in other visitors' browsers. It is a classic web attack, delivered by an AI. The fix is old and simple: never trust text, even when a model wrote it.
Check yourself
You get a 429 response. What should you do?
Not quite. Backoff gives the limit time to reset without hammering the API.
Where should an API key live?
Not quite. Anything shipped to a browser can be copied by anyone.
A model's reply contains an HTML script tag and your page inserts it as HTML. What is the risk?
Not quite. Treat model output as untrusted. Sanitize it or render markdown safely.
You got 0 of 3 right.
Go deeper
- DocsAPI errorsDocs
Anthropic · Status codes and what each means.
- Docs
OWASP · Why unsanitized output is dangerous.
- RepoDOMPurifyRepo
cure53 · The standard HTML sanitizer for the browser.
- Article
Amazon Builders' Library · How to retry without making an outage worse.
Spotted a mistake or something unclear? Report it on GitHub.