Repository navigation
fix(rpc-client): retry rate-limit responses returned as an HTTP 200 string result - #582
Closed
SQD-Trevor-Agent wants to merge 1 commit into
Closed
SQD-Trevor-Agent wants to merge 1 commit into
SQD-Trevor-Agent wants to merge 1 commit into
Conversation
…tring result Some providers (notably via an erpc proxy) signal rate limiting with an HTTP 200 whose JSON-RPC `result` is a plain string like "You've been rate limited, please upgrade your plan." instead of a 429 or a JSON-RPC error object. This bypassed the retry machinery and hit the result validator, throwing a non-retryable DataValidationError that crash-looped the dumper. Treat such a result as a RetryError so the existing retry/backoff (and provider failover) handle it like a 429.
Contributor
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.
Cause (proven)
dump-tac-mainnet-0was inCrashLoopBackOff(721 restarts). The archive's highest block was frozen at 25,979,220 while the chain head is 26,125,782 (~146k behind), so the hotblocks rolling window advanced past the frozen archive block and theportal_hotblocks_gapalert fired fortac-mainnet.The fatal error, from the dump pod's previous-container logs:
When rate-limited, the upstream (uniblock, via the erpc proxy) returns HTTP 200 with the JSON-RPC
resultset to a plain rate-limit string instead of a429status or a JSON-RPCerrorobject. Because there is nores.errorand the status is 200, the response bypasses every retryable path inRpcClient(isConnectionErrorhandles 429s,RetryError, rate-limitRpcErrors, etc.) and goes straight to the result validator.getResultValidator(Receipt)then throws a non-retryableDataValidationError(… is not an object). The dumper runs withretryAttempts: MAX_SAFE_INTEGER, so a genuine 429 retries forever with backoff — but this validation error propagates and crash-loops the process.Fix
In
RpcClient.receiveResult, detect a rate-limit signal delivered as a 200 stringresultand throw aRetryErrorbefore validation.RetryErroris already treated as a connection error, so the existing retry/backoff (and erpc provider failover to ankr) handle it exactly like a 429. Detection is precise (typeof result === 'string' && /rate limit/i, matching the existingisRateLimitErrorregex), so it only catches rate-limit strings and does not convert genuine data-corruption validation failures into retries.This lives in
rpc-client(notevm-rpc) because the signal is a transport/provider concern that affects every method (blocks, receipts, traces, logs) and every chain, not just the evm receipts path.Related to #551 (retry transient malformed block results) — same class of "transient upstream response crash-loops the dumper", but a different cause and path: #551 wraps the
eth_getBlockByNumberblock validator for an object that is missing a field; this handles a rate-limit string result on any method, centrally in the client.Test (red→green)
util/rpc-client/src/client.rate-limit.test.tsspins up a local HTTP server returning the exact{result: "You've been rate limited…"}200 response and assertsclient.call('eth_getTransactionReceipt', …)rejects with aRetryError, plus unit coverage for theisRateLimitResultpredicate.1 failed—expected Error: server returned unexpected result: … to be an instance of RetryError(reproduces the production crash)10 passedtsc --noEmitclean;rush build --to @subsquid/rpc-clientgreen.Falsification
If
dump-tac-mainnet-0still exits with aDataValidationErrorafter this change, the malformed result is not a rate-limit string (the regex missed it) — capture the newrpcResponseand widen the signature. If the dump merely lags without crashing, the remaining issue is provider capacity (uniblock rate-limiting), which is a provider/ops matter (swap/upgrade), not this code path.Provider note (operator action, not part of this PR)
The trigger is uniblock persistently rate-limiting tac-mainnet (chainId 239) via erpc. This code fix stops the crash-loop; if rate-limiting is sustained the archive will still lag. An operator should consider swapping/upgrading the tac-mainnet erpc upstream — but per convention that mitigation is handled out-of-band, not in this PR.