Repository navigation
fix(transport): retry idempotent requests on network failures - #205
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
It changes the retry/latency behavior of the core shared transport layer used by two production frontends, so a human should sign off on the operational trade-offs even though the implementation is clean and well-tested.
Review effort: Balanced
Findings: None
What changed in this PR
This PR hardens the shared transport layer so a dropped browser↔Cloudflare keep-alive connection (ERR_CONNECTION_CLOSED) no longer surfaces straight to the connectivity popup. Previously fetchWrapper swallowed the thrown fetch error via handleError, so React Query's own retry never got a chance to fire. The fix adds an internal, bounded retry around fetch for safe/idempotent methods only. This is a focused behavior change to @behindthemusictree/app-kit's transport module, which is consumed by both grow-the-music-tree-frontend and hear-the-music-tree-frontend.
Changes:
- Added
fetchWithNetworkRetrythat retriesGET/HEADup to twice (300ms, then 900ms) only whenfetchitself throws, skipping aborted signals, error responses, and non-idempotent methods. - Added tests covering recovery-after-retry, no-retry-for-POST/abort/error-response, and retry-exhaustion (3 total attempts), updating existing network-failure tests to drive fake timers.
- Added a
CHANGELOG.mdentry under[Unreleased] → Fixed.
| File | Description |
|---|---|
packages/app-kit/src/transport/fetch-wrapper.ts |
Introduces bounded network retry for idempotent methods around the fetch call. |
packages/app-kit/src/transport/fetch-wrapper.test.ts |
Adds retry-behavior tests and adapts existing tests to fake timers. |
CHANGELOG.md |
Documents the retry behavior under [Unreleased]. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
A dropped browser↔Cloudflare connection (
ERR_CONNECTION_CLOSEDon grow staging) surfaced straight to the connectivity popup: React Queryretrycan't fire sincefetchWrapperswallows the error viahandleError.fetchWrappernow retries GET/HEAD up to twice (300ms, 900ms) only whenfetchthrows. Error responses, aborted signals, and other methods are never retried.🤖 Generated with Claude Code