What happened?
classifyGoogleError() parses RetryInfo.retryDelay via parseDurationInSeconds(), then guards with if (parsedDelay) — treating a successful parse of "0s"/"0ms" as a failure because 0 is falsy. delaySeconds stays undefined, so a RATE_LIMIT_EXCEEDED error carrying "retry immediately" semantics falls into the terminal branch instead of the retryable one: retries are aborted and the fallback/credits flow fires for a routine rate limit.
Affected code
packages/core/src/utils/googleQuotaErrors.ts:340-345:
if (retryInfo?.retryDelay) {
const parsedDelay = parseDurationInSeconds(retryInfo.retryDelay);
if (parsedDelay) { // <-- 0 is falsy
delaySeconds = parsedDelay;
}
}
packages/core/src/utils/googleQuotaErrors.ts:103-113:
function parseDurationInSeconds(duration: string): number | null {
// ...
if (duration.endsWith('s')) {
const seconds = parseFloat(duration.slice(0, -1));
return isNaN(seconds) ? null : seconds; // returns 0 for "0s"
}
The same truthiness guard recurs at ~line 411 (retryInfo?.retryDelay && delaySeconds).
How can this be reproduced?
Return a 429 with body containing ErrorInfo { reason: "RATE_LIMIT_EXCEEDED" } and RetryInfo { retryDelay: "0s" }. Observed: TerminalQuotaError (terminal). Expected: RetryableQuotaError with immediate/small delay.
What did you expect to happen?
A parsed delay of exactly 0 should be honored ("retry now"), not treated as unparseable.
Suggested direction
Use the documented sentinel contract: const parsed = parseDurationInSeconds(...); if (parsed !== null) { delaySeconds = parsed; } at both occurrences.
Found by source audit on current main (commit 5411f113c); platform-independent. No open issue/PR covering this was found (searched: retry delay rate limit terminal).
What happened?
classifyGoogleError()parsesRetryInfo.retryDelayviaparseDurationInSeconds(), then guards withif (parsedDelay)— treating a successful parse of"0s"/"0ms"as a failure because0is falsy.delaySecondsstaysundefined, so aRATE_LIMIT_EXCEEDEDerror carrying "retry immediately" semantics falls into the terminal branch instead of the retryable one: retries are aborted and the fallback/credits flow fires for a routine rate limit.Affected code
packages/core/src/utils/googleQuotaErrors.ts:340-345:packages/core/src/utils/googleQuotaErrors.ts:103-113:The same truthiness guard recurs at ~line 411 (
retryInfo?.retryDelay && delaySeconds).How can this be reproduced?
Return a 429 with body containing
ErrorInfo { reason: "RATE_LIMIT_EXCEEDED" }andRetryInfo { retryDelay: "0s" }. Observed:TerminalQuotaError(terminal). Expected:RetryableQuotaErrorwith immediate/small delay.What did you expect to happen?
A parsed delay of exactly
0should be honored ("retry now"), not treated as unparseable.Suggested direction
Use the documented sentinel contract:
const parsed = parseDurationInSeconds(...); if (parsed !== null) { delaySeconds = parsed; }at both occurrences.Found by source audit on current
main(commit5411f113c); platform-independent. No open issue/PR covering this was found (searched: retry delay rate limit terminal).