Skip to content
skyl

ParseRetryAfter

Interprets a Retry-After header, in either wire form.

Retry-After comes in two shapes and providers use both. ParseRetryAfter handles each, and never hands you a value that would make backoff nonsensical.

Reference#

func ParseRetryAfter(v string) time.Duration

Returns

Header valueReturns
6060 seconds
1.51.5 seconds — fractional values are accepted
Wed, 04 Aug 2026 21:00:00 GMTThe remaining duration until that time
(a date in the past)0
garbage0
(empty)0
-50

Caveats

  • It never returns a negative duration. A date already in the past yields zero, so backoff falls back to its computed delay rather than doing something nonsensical.
  • Zero means "no usable hint", not "retry immediately".
  • Leading and trailing whitespace is trimmed.
  • The value skyl honours is separately bounded by WithRetryAfterCap, defaulting to 5 minutes.

Usage#

In an adapter

e := skyl.NewError(p.Name(), res.StatusCode, skyl.ErrRateLimit, msg, raw)
e.RetryAfter = skyl.ParseRetryAfter(res.Header.Get("Retry-After"))
return nil, e
e := skyl.NewError(p.Name(), res.StatusCode, skyl.ErrRateLimit, msg, raw)
e.RetryAfter = skyl.ParseRetryAfter(res.Header.Get("Retry-After"))
return nil, e

Reading the hint as a caller

goCompiles
var e *skyl.Error
if errors.As(err, &e) && e.RetryAfter > 0 {
	log.Printf("%s asked for %s", e.Provider, e.RetryAfter)
}
var e *skyl.Error
if errors.As(err, &e) && e.RetryAfter > 0 {
	log.Printf("%s asked for %s", e.Provider, e.RetryAfter)
}

Propagating it to your own callers

goCompiles
if e != nil && e.RetryAfter > 0 {
	// So your callers back off in step with the real window rather than guessing.
	w.Header().Set("Retry-After", strconv.Itoa(int(e.RetryAfter.Seconds())))
}
if e != nil && e.RetryAfter > 0 {
	// So your callers back off in step with the real window rather than guessing.
	w.Header().Set("Retry-After", strconv.Itoa(int(e.RetryAfter.Seconds())))
}

Troubleshooting#

RetryAfter is zero on a 429

The provider did not send the header, or sent something unparseable. skyl falls back to its computed backoff, which is the correct behaviour — a zero is not "retry now".

A retry waited five minutes

That is WithRetryAfterCap's default being reached: the provider asked for at least that long. Lower the cap if your latency budget cannot absorb it.

Edit this page on GitHub