You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Retry-After header honored only on 429; 503/504/408 silently ignored #322
_map_error parses Retry-After only inside the status == 429 branch and attaches the value exclusively to KalshiRateLimitError. For 503 (Kalshi's own scheduled-maintenance pattern, AWS ALB throttles, Cloudflare "Under Attack" mode), 504, and 408 — all retryable status codes — the header is silently ignored, and the retry loop falls back to _compute_backoff capped at retry_max_delay (default 30 s). A 503 with Retry-After: 60 is therefore re-hammered every couple of seconds while the upstream is asking for a full minute.
RFC 7231 §7.1.3 explicitly defines Retry-After as the canonical signal for 503 and permits it on any retryable 4xx. The parsing logic at _base_client.py:93-129 is otherwise correct (battle-tested by #267) — it just isn't reachable from the 5xx/timeout path.
Evidence
kalshi/_base_client.py:92-132 — header only parsed in the 429 branch.
kalshi/_base_client.py:137-140 — 408/504 → KalshiTimeoutError with no retry_after; 5xx → KalshiServerError, same.
kalshi/_base_client.py:391-394 & 601-604 — retry loop only consults retry_after on KalshiRateLimitError
kalshi/errors.py:59 — retry_after defined only on KalshiRateLimitError.
Suggested fix
Extract a _parse_retry_after(response) -> float | None helper from the 429 branch and call it from every retryable error path. Expose retry_after: float | None on the shared KalshiError base (or at least on KalshiServerError and KalshiTimeoutError). The retry loop becomes one line:
Activity
fix(transport): Retry-After on 5xx/408 + jittered + success body cap (#…