Finding
valhalla/jawn/src/lib/proxy/ProviderClient.ts uses async-retry with exponentialDelay for 429 responses. No Retry-After header consulted. 429 and 5xx treated identically.
await retry(
async (bail, attempt) => {
const res = await callProvider(callProps);
if (res.status === 429 || (res.status < 600 && res.status >= 500)) {
throw new Error(`Status code ${res.status}`);
}
},
{ retries: 3 }
);
The consequence
Every LLM call proxied through Helicone inherits this behavior. When a provider returns Retry-After: 60, all three retries fire inside the cooldown window.
This is a platform-level finding — not isolated to one customer. Every company routing LLM calls through Helicone inherits the retry behavior.
For an observability platform, the irony is real:
Helicone doesn’t observe its own provider rate limit signals.
Expected behavior
- Read
Retry-After when present before computing delay
- Use header value as minimum retry delay
- Fall back to
exponentialDelay only when absent
- Distinguish 429 from 5xx at the classification boundary
Related pattern
retry-after-ignored-exponential-backoff
Corpus reference: https://github.com/SirBrenton/pitstop-truth
Finding
valhalla/jawn/src/lib/proxy/ProviderClient.tsusesasync-retrywithexponentialDelayfor 429 responses. NoRetry-Afterheader consulted. 429 and 5xx treated identically.The consequence
Every LLM call proxied through Helicone inherits this behavior. When a provider returns
Retry-After: 60, all three retries fire inside the cooldown window.This is a platform-level finding — not isolated to one customer. Every company routing LLM calls through Helicone inherits the retry behavior.
For an observability platform, the irony is real:
Helicone doesn’t observe its own provider rate limit signals.
Expected behavior
Retry-Afterwhen present before computing delayexponentialDelayonly when absentRelated pattern
retry-after-ignored-exponential-backoffCorpus reference: https://github.com/SirBrenton/pitstop-truth