fix(evm-rpc): retry transient provider and proxy errors instead of failing - #584
Merged
Merged
Conversation
This was referenced Sep 29, 2026
mo4islona
force-pushed
the
fix/evm-rpc-retry-transient-errors
branch
2 times, most recently
from
September 29, 2026 15:53
267d915 to
f4b33c1
Compare
Providers and load-balancing proxies report transient failures in shapes the classifier did not recognize: a rate limit under code -32429, error text or null as the `result` of a 200 response, upstream or head-lag failures under generic codes, and Cloudflare 520-524. evm-dump exited on them and the data service restarted ingestion, although the same call succeeds when repeated. Plain errors are classified by message, and "please retry"-like phrases count only when neither the code nor the message blames the request. A proxy normalizes its outer code and message, so its errors are judged by the causes nested under `data`; an unsupported or ignored method, or rejected credentials, stays fatal. Closes #583 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Errors that used to stop evm-dump or restart real-time ingestion are now retried, so a dump stuck on a persistently failing endpoint no longer shows up as restarts. Counting retried errors by kind, per endpoint, keeps that condition alertable. In the data service the RPC clients live in worker threads, so their counts are read on each scrape and the final counts of closed workers are kept. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
mo4islona
force-pushed
the
fix/evm-rpc-retry-transient-errors
branch
from
September 29, 2026 21:00
f4b33c1 to
9515ca6
Compare
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.
Summary
Several transient failures from RPC providers and load-balancing proxies were classified as fatal.
evm-dumpexited on them and the data service restarted ingestion, although the same call succeeds when repeated. The error shapes, with handling before and after this change and the ones left fatal on purpose, are listed in #583.Changes
EvmRpcClient.isConnectionError:-32429, and the messagesthroughput limitandexceeded … capacity;-32700,-32600,-32601,-32602) nor the message blames the request, so advice like "please retry with a smaller block range" stays fatal;data, not by its outer code and message, which the proxy normalizes. An unsupported-method, ignored-method, unauthorized, client-side or execution cause keeps the error fatal;retryInternalServerErrors.nullwhere a value is due, or a non-hex string inresult, throwsRetryErrorinstead ofDataValidationError. Malformed objects still fail. See the trade-off below.Metrics
Errors that used to stop
evm-dumpor restart ingestion are now retried, so an endpoint that keeps failing no longer shows up as restarts. To keep that visible:sqd_chain_rpc_retried_errors_total{url, kind}, inevm-dumpand the EVM data service.kindis one ofrate_limit,transient,no_result,internal,timeout,http,connection. Outside EVM it can also beretryorother.urlis redacted like in the other RPC metrics.reduceBatchOnRetrysplits in halves instead of retrying.sqd_hotblocks_ingestion_restarts_total{reason}:reasonis the name of the error that stopped ingestion, orfork, orended.RpcClient.getRetryKind()andgetMetrics().retriedErrorsinrpc-client.EvmRpcClientrefines the kinds.Trade-off
nullor error text inresultmeans the node does not have the data. Usually that is a backend lagging the head, which a retry fixes, but it can also be data the node will never have, for example pruned transaction history. The response does not tell these apart.These retries draw on the client's shared retry budget, which
evm-dumpsets unbounded. So on permanently missing data the dump now retries with backoff, logging each attempt as a connection failure and counting it asno_result, instead of exiting. In the data service the budget is 5 attempts, after which the error surfaces as before. A per-request cap on no-data retries is left as a TODO ingetResultValidator.Related
/rate limit/check on string results misses other error text delivered the same way, for example an upstream connect-error page.ErrUpstreamsExhausted/ErrFailsafeRetryExceeded, and the same wrapper also arrives for a method the endpoint will never serve, so that error would be retried forever. Here the nested cause decides.Tests
evm/evm-rpc/test/rpc.transient-errors.test.ts:nullin place of a block throwsRetryError, a malformed block throwsDataValidationError, and the call recovers on retry;util/rpc-client/src/client.connection-error.test.ts: HTTP status classification, retry kinds, and the counts against a local server: every retry is counted, while the error that fails a request, or any error when retries are off, is not.util/util-internal-data-service/src/rpc-metrics.test.ts: summing across workers, keeping the counts of a closed worker, a hung or dead worker, a worker that stops answering keeping its last counts, a worker removed during a scrape counted once, the Prometheus output, and the last values surviving a failed read.util/util-internal-data-service/src/data-service.test.ts: a failed ingestion session increments the restart counter by error name.util-internal-dump-cliandevm-data-servicehave no test setup. Their wiring is checked by compilation and by the tests of the pieces they call.rpc-client: 34 passed;evm-rpc: 325 passed, 29 skipped;util-internal-data-service: 14 passed;tsc: clean forrpc-client,util-internal-data-service,evm-rpc(withtsconfig.test.json),evm-data-serviceandutil-internal-dump-cli.🤖 Generated with Claude Code