Skip to content

fix(http): back off 2^attempt when a 429's Retry-After can't be parsed - #136

Closed
Ankur Gupta (ankur-pattern) wants to merge 2 commits into
mainfrom
retry-after-backoff-fallback
Closed

Ankur Gupta (ankur-pattern) wants to merge 2 commits into
mainfrom
retry-after-backoff-fallback

Conversation

@ankur-pattern

@ankur-pattern Ankur Gupta (ankur-pattern) commented Oct 28, 2026 •

Copy link
Copy Markdown

Summary

Fixes the 429 backoff in the http task when Retry-After can't be used. Previously, a 429 with a Retry-After header that didn't parse as seconds (for example an HTTP date, or a negative value) returned from handleBackoff without sleeping. Values like 5m or 1u were worse: time.ParseDuration(value + "s") read them as 5ms and 1µs. The next attempt then fired immediately, using up max_retries within milliseconds. It now falls back to the existing 2^attempt backoff, the same as a 429 with no Retry-After.

A valid Retry-After (delay-seconds, a non-negative integer per RFC 9110) is honored exactly as before. No config changes.

Changes

  • http/retry.go: new retryAfter(response) returns the Retry-After delay for a 429 when the header is a non-negative integer number of seconds (strconv.ParseUint, surrounding spaces allowed). handleBackoff sleeps that delay when it's valid, and otherwise sleeps 2^attempt.

Testing

  • go vet -copylocks=false ./... and go test -race ./... pass locally, the same as CI.
  • Checked locally with unit tests (not committed) for retryAfter (valid, padded, missing, negative, unparseable, 5m, 1u, 1.5, HTTP date, non-429, nil response) and the 429 sleep in handleBackoff (valid Retry-After honored; unparseable and missing both back off 2^attempt).

Replaces #135, which also added max_throttle_retries; that part isn't needed.

@ankur-pattern
Ankur Gupta (ankur-pattern) requested a review from a team as a code owner October 28, 2026 09:22
@ankur-pattern

Copy link
Copy Markdown
Author

Closing per review: Caterpillar already honors a valid Retry-After, and our callers (rate-gate, Amazon) never send an unparseable one, so this isn't needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants