Repository navigation
feat(fetch): retry uncharged busy answers so a brief capacity wait does not fail the command - #64
Draft
gonzalo-zenrows wants to merge 1 commit into
Draft
gonzalo-zenrows wants to merge 1 commit into
gonzalo-zenrows wants to merge 1 commit into
Conversation
…es not fail the command Fetch and Extract sent each request once. A 429, 502, 503 or 504 (none of which the gateway charges) or a network error failed the command, and 503 RESP009 fell into the generic branch that advised the most expensive manual configuration. - Retry those answers up to --retries times (default 2; 0 sends once). Each wait honours Retry-After in whole seconds, capped at 30 s, or is full jitter: uniform(0.25 s, min(30 s, 1 s x 2^attempt)). No retry starts once the time spent plus the next wait would pass 90 s. - Never retry the client timeout: the API keeps working and may still charge the request. Never retry other 4xx, or a 429 that says the account is out of credits. The Extract-not-enabled fallback to autoparse is unchanged. - 503 RESP009 is now SERVICE_BUSY: not charged, retry shortly, no escalation. - The gateway budget is 180 s for the whole request, not 90 s. The client timeout stays at 120 s; the timeout error now says the API may still finish and charge the request, and suggests a value above the budget. - Retry-After parsing and the delay schedules move to src/core/backoff.ts; Crawl keeps its own schedule unchanged.
This was referenced Oct 16, 2026
Draft
Author
|
Same retry policy across all clients (429/502/503/504 and pre-response connection errors; 2 retries by default; full jitter; Retry-After capped at 30 s; 90 s total budget; timeouts never retried):
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
zenrows fetchandzenrows extractsent each request once. When the gateway was briefly busy (429, 502, 503, 504, none of which is charged) or the network dropped, the command failed. A 503RESP009("at capacity, retry shortly") fell into the generic branch that told agents to rerun with--manual --js-render --premium-proxy, the most expensive configuration, which does not help.This applies the retry policy the Zenrows Fetch/Extract clients share.
What changes
--retries <n>times (new flag onfetchandextract, default 2,0sends once).Retry-Afterin whole seconds, capped at 30 s. Otherwise full jitter:uniform(0.25 s, min(30 s, 1 s × 2^attempt)), withattemptthe 0-based retry index (first retry ≤ 1 s, second ≤ 2 s).--timeout; the API keeps working and may still charge a 200), any other 4xx, and a 429 whose body says the account is out of credits (it answers the same until the account changes). TheEXTRACT_DOMAIN_NOT_ENABLEDfallback to--autoparseis unchanged; a test pins that it spends no retries.SERVICE_BUSY. A new error for 503RESP009, withserver_code,retry_after, alikely_causethat says the request was not charged (for Extract: the extraction service is still preparing this layout), and anext_actionto retry the same command shortly without escalating.SERVER_BUDGET_MSis now 180 s (the whole request: auth, fetch, parse), not 90 s. TheREQUEST_TIMEOUTerror now says the API may still finish and charge the request, and suggests a--timeoutabove 180 s.Retry-Afterparsing and the delay schedules move tosrc/core/backoff.ts. Crawl uses it with its own schedule unchanged (250 ms × 2^attempt, capped at 10 s, ±20%).--retriesin thefetch/extracthelp, the README Fetch section, and theprotected-fetchandtrace-debugskills.Decision for the reviewer
The default client timeout stays at 120 s, below the gateway's 180 s budget. The old test pinned "client timeout above server budget" so the API could always answer with its own error. That no longer holds, and I replaced the test rather than raise the default, because changing the default timeout was out of scope. Raising it above 180 s (for example to 200 s) would restore that property. It is a one-line change if you want it here.
Testing
npm run typecheck && npm test: 402 tests, 0 failures (Node 26 locally; CI runs Node 20 and 22). Newtests/fetch-retry.test.tsuses a stubbedfetchand an injected sleep and clock. It covers: jitter bounds, including on 503;Retry-Afterhonoured and capped; the 90 s budget; no retry on 400/401/404/422 or on a timeout abort; the default of 2;--retries 0; flag validation; the unchanged autoparse fallback; and theSERVICE_BUSYshape for Fetch and Extract.Not verified against the live API.