There's a strange economics problem in testing OpenAI integrations: the error path you most need to verify is the one that costs money to trigger. To see a real 429, you either hammer the API with paid requests until you trip the limit, or you wait for production traffic to do it for you.
So most codebases ship retry logic that has never executed. It was written by reading the docs (or by an agent that read the docs), reviewed by someone who also read the docs, and merged. The first real execution happens during a launch spike — precisely when you can least afford to discover that your backoff doubles the request volume instead of halving it.
The three 429s that aren't the same error
OpenAI's 429 is three distinct failures wearing one status code:
rate_limit_exceeded(RPM/TPM) — transient; correct response is exponential backoff honoringretry-after, then retryinsufficient_quota— not transient; retrying is pure waste, the correct response is failing over or alerting a human- Token-per-minute exhaustion on long prompts — retrying the same oversized request will 429 forever; the fix is chunking, not waiting
Handler code that treats all three as "sleep and retry" passes every code review and works fine in dev, where none of them ever fire. Then insufficient_quota hits at 2am and your worker spends six hours politely retrying a bill that needs a credit card update.
Mocks don't save you here either — a mock returns the 429 you wrote into it, in the shape you assumed. The bugs live in the shapes you didn't assume.
Trigger the failure deliberately
The FetchSandbox OpenAI sandbox models the request lifecycle including the failure modes — so you make your code experience a 429 on demand, without spending a cent of real quota. With the MCP server wired into Cursor or Claude Code:
./fetchsandbox run my chat completion flow and inject rate_limit_exceeded
on the second call — verify the backoff honors retry-after, then inject
insufficient_quota and verify we alert instead of retrying
The run trace shows each request, the injected error, and what your client actually did next — retry timing included. If your "exponential" backoff turns out to be constant, that's visible in the timestamps, in a receipt URL you can drop in the PR.
The general rule
Error-path code that has never run is untested code, no matter how many green checkmarks sit on the happy path. For paid APIs, that's usually rate limits, quota exhaustion, and timeout-then-duplicate. Run each one on purpose, once, before production runs them for you at volume.
Related: you can't code-review a webhook retry — the same class of bug, on the inbound side.