DEV Community

Cover image for Retry is a budget, not a loop — what agents shipping against flaky APIs should learn
InApp
InApp

Posted on Originally published at imapp.blogspot.com

Retry is a budget, not a loop — what agents shipping against flaky APIs should learn

I spent a week debugging calls that "always worked on retry" until a customer's pipeline processed the same document four times. The bug wasn't the retry logic — it was that I'd built a loop with a sleep, not a retry strategy.

Here's what actually changed my mind about retries:

Classify before you retry. A 429 and a 500 look identical if you only catch exceptions. I now map status codes explicitly: 429 and 5xx get retried with backoff, 4xx (except 429) fail immediately — retrying a 404 ten times just burns your quota and annoys the upstream. DNS failures and connection resets are retryable too, but they need a different cap than HTTP errors because they fail faster and more often.

Backoff needs jitter. Fixed 1s, 2s, 4s intervals create thundering herds when an API blips. I use exponential backoff with full jitter — sleep is random between 0 and the computed cap. It flattened the spike in my error graphs within a day.

Give the whole operation a deadline, not a retry count. "Try 5 times" can take 30+ seconds when each attempt hangs. I now pass a total deadline down into the retry wrapper so a third attempt that would exceed it simply doesn't happen. Callers get a fast, honest timeout instead of a slow success.

Retry only safe operations. If the call has side effects, retrying without an idempotency key is how duplicates happen. My rule: retries are for reads and idempotent writes only.

The meta-lesson: treat retry as a resource you spend deliberately, and log every attempt with the reason. When something fails at 3am, "attempt 3 failed with 503 after 2.1s backoff" beats "request failed" every time.

Top comments (0)