Summary
RetryWithExponentialBackoff appears to handle Retry-After incorrectly in a way that can cause retries to happen earlier than the server requested.
There seem to be two independent issues in the current implementation:
- It reads
headers['Retry-After'] instead of the normalized lowercase header key.
- It compares elapsed time in milliseconds against a parsed
Retry-After value that is treated like seconds.
Current Behavior
In api/apis.ts, the retry logic currently does this:
const responseHeaders = headers || {}
lastRequestRetryAfter = responseHeaders['Retry-After']
if (lastRequestRetryAfter) {
lastRequestRetryAfter = parseInt(lastRequestRetryAfter, 10)
}
and later:
const retryAfterValueLapsed = (!lastRequestRetryAfter ||
currentTime - lastRequestTimestamp > lastRequestRetryAfter)
This creates two problems:
1) Header lookup likely misses the value
In JavaScript object access, headers['Retry-After'] and headers['retry-after'] are different keys.
For example:
const headers = { 'retry-after': '10' }
headers['Retry-After'] // undefined
headers['retry-after'] // '10'
So the code can ignore the server-provided Retry-After value entirely.
2) Time units are mismatched
Date.now() values are in milliseconds, but a numeric Retry-After header is parsed as seconds.
So if the server returns:
then the code effectively compares:
instead of waiting roughly 10 seconds.
That means retries can happen after ~10ms rather than ~10s.
Expected Behavior
If a retryable response includes Retry-After, the client should:
- read the header value reliably
- interpret numeric values using the correct units
- avoid retrying until the server-requested delay has elapsed
Evidence
Relevant code in api/apis.ts:
const responseHeaders = headers || {}
lastRequestRetryAfter = responseHeaders['Retry-After']
if (lastRequestRetryAfter) {
lastRequestRetryAfter = parseInt(lastRequestRetryAfter, 10)
}
lastRequestTimestamp = Date.now()
and:
const retryAfterValueLapsed = (!lastRequestRetryAfter ||
currentTime - lastRequestTimestamp > lastRequestRetryAfter)
Reproduction
- Use
RetryWithExponentialBackoff with a request that receives a retryable response such as 429.
- Return a
Retry-After header with a numeric value, e.g. 10.
- Observe that:
- the value may be ignored if the header is exposed as lowercase
- even if used, the elapsed-time comparison is done in milliseconds vs seconds
- The next retry can happen much earlier than intended.
Duplicate Check
- Open issues checked: no direct match found
- Closed issues checked: found
#7, but that older issue was closed as fixed in 2.0.0 and does not appear to describe this current logic problem directly
- Recent PRs checked: no direct match found
Summary
RetryWithExponentialBackoffappears to handleRetry-Afterincorrectly in a way that can cause retries to happen earlier than the server requested.There seem to be two independent issues in the current implementation:
headers['Retry-After']instead of the normalized lowercase header key.Retry-Aftervalue that is treated like seconds.Current Behavior
In
api/apis.ts, the retry logic currently does this:and later:
This creates two problems:
1) Header lookup likely misses the value
In JavaScript object access,
headers['Retry-After']andheaders['retry-after']are different keys.For example:
So the code can ignore the server-provided
Retry-Aftervalue entirely.2) Time units are mismatched
Date.now()values are in milliseconds, but a numericRetry-Afterheader is parsed as seconds.So if the server returns:
Retry-After: 10then the code effectively compares:
instead of waiting roughly 10 seconds.
That means retries can happen after ~10ms rather than ~10s.
Expected Behavior
If a retryable response includes
Retry-After, the client should:Evidence
Relevant code in
api/apis.ts:and:
Reproduction
RetryWithExponentialBackoffwith a request that receives a retryable response such as429.Retry-Afterheader with a numeric value, e.g.10.Duplicate Check
#7, but that older issue was closed as fixed in2.0.0and does not appear to describe this current logic problem directly