Skip to content

fix(shared): harden fetchWithRetry — backoff cap, 429, Retry-After, abort handling (#209)#212

Merged
williamzujkowski merged 1 commit into
mainfrom
fix/retry-hardening
Jun 23, 2026
Merged

fix(shared): harden fetchWithRetry — backoff cap, 429, Retry-After, abort handling (#209)#212
williamzujkowski merged 1 commit into
mainfrom
fix/retry-hardening

Conversation

@williamzujkowski

Copy link
Copy Markdown
Collaborator

Addresses the retry.ts items from #209 (the packages/shared review). The rate-limiter concurrency fix is larger and tracked separately under the same issue.

Changes to fetchWithRetry

  • Cap backoff at MAX_BACKOFF_MS — the constant was defined and unit-tested but never applied, so backoff grew unbounded for large maxRetries/baseDelayMs.
  • Retry 429 Too Many Requests — previously only 5xx retried, so a rate-limited OLRC/CourtListener response failed immediately instead of backing off.
  • Honor Retry-After (delta-seconds or HTTP-date) on retryable responses, capped at MAX_BACKOFF_MS.
  • Don't retry AbortError — a caller that cancels (timeout / abort signal) now gets the abort back immediately rather than N pointless retries masking it as "Network error after N attempts".
  • Preserve { cause } so the underlying error (ECONNRESET, stack, code) survives for diagnostics.

Refactored into small helpers (isRetryableStatus, backoffDelayMs, retryAfterMs).

Verification

New tests: 429 retry, abort short-circuit (callCount === 1), cause preservation. Existing retry tests (5xx, network, 404 non-retryable, options passthrough) unchanged.
pnpm --filter @civic-source/shared test (28), fetcher (123), annotator (76) all pass; pnpm build 8/8.

🤖 Generated with Claude Code

…orts)

Addresses the retry.ts items from the packages/shared review (#209):

- Cap backoff at MAX_BACKOFF_MS (was defined + tested but never applied,
  so backoff was unbounded for large maxRetries/baseDelayMs).
- Retry 429 Too Many Requests (previously only 5xx), so a rate-limited
  OLRC/CourtListener response backs off instead of failing immediately.
- Honor a Retry-After header (delta-seconds or HTTP-date) on retryable
  responses, capped at MAX_BACKOFF_MS.
- Do NOT retry AbortError — a caller that cancels (timeout/abort signal)
  now gets the abort back immediately instead of N pointless retries that
  mask it as a network error.
- Preserve the underlying error via `{ cause }` so ECONNRESET/stack/code
  survive for diagnostics.

New tests cover 429 retry, abort short-circuit (callCount === 1), and
cause preservation. shared 28 / fetcher 123 / annotator 76 pass; build 8/8.

Refs #209 (rate-limiter FIFO concurrency fix tracked separately)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@williamzujkowski williamzujkowski requested a review from a team as a code owner June 23, 2026 04:56
@williamzujkowski williamzujkowski merged commit 6e632a1 into main Jun 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant