Skip to content

ProviderClient.ts — 429 and 5xx use same exponential backoff, Retry-After never consulted #5672

Description

@SirBrenton

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions