ECA classifies all HTTP 429 responses as :rate-limited and retries them up to 10 times with exponential backoff. But providers use 429 for fundamentally different errors — e.g. OpenAI returns 429 for both rate_limit_exceeded (transient) and usage_limit_reached (billing/quota, not transient).
Example: OpenAI returns 429 {"error":{"type":"usage_limit_reached","message":"The usage limit has been reached","resets_in_seconds":7139}}. ECA shows "Rate limited" and retries 10 times, wasting minutes on an error that resets in ~2 hours.
What would be nice to change
- New non-retryable error type (e.g.
:billing) for quota/billing errors
- Body patterns to detect them:
usage_limit_reached, insufficient_quota, billing_hard_limit_reached, etc.
- 429 classifier branch must check body before falling back to
:rate-limited
- Same patterns in
classify-by-message (fallback for SSE/exception errors)
- UI label — show "Usage limit reached" instead of "Rate limited"
- Nice-to-have: if the provider includes reset info (e.g.
resets_in_seconds), surface it to the user
ECA classifies all HTTP 429 responses as
:rate-limitedand retries them up to 10 times with exponential backoff. But providers use 429 for fundamentally different errors — e.g. OpenAI returns 429 for bothrate_limit_exceeded(transient) andusage_limit_reached(billing/quota, not transient).Example: OpenAI returns
429 {"error":{"type":"usage_limit_reached","message":"The usage limit has been reached","resets_in_seconds":7139}}. ECA shows "Rate limited" and retries 10 times, wasting minutes on an error that resets in ~2 hours.What would be nice to change
:billing) for quota/billing errorsusage_limit_reached,insufficient_quota,billing_hard_limit_reached, etc.:rate-limitedclassify-by-message(fallback for SSE/exception errors)resets_in_seconds), surface it to the user