Skip to content

fix: Raise Etherscan client errors and stop flaky network tests - #2760

Merged
Uxío (Uxio0) merged 3 commits into
mainfrom
fix/etherscan-flaky-tests
Sep 24, 2026
Merged

Uxío (Uxio0) merged 3 commits into
mainfrom
fix/etherscan-flaky-tests

Conversation

@Uxio0

Copy link
Copy Markdown
Member

Why

test_etherscan_get_contract_metadata fails often in CI with AttributeError: 'NoneType' object has no attribute 'name', and passes when it is run again. Three causes:

  1. The Etherscan client only detected the Max rate limit reached message. Other rate limit messages (Max calls per sec rate limit reached (5/sec), the daily limit), HTTP 429 and any other API error returned None. So the test got None instead of EtherscanRateLimitError, and the skipTest never ran.
  2. @pytest.mark.flaky(reruns=5, delay=2): delay is not a valid kwarg for pytest-rerunfailures, it is ignored. Reruns ran with no delay, inside the same rate limit window.
  3. The 4 jobs of the CI matrix call Etherscan at the same time with the same API key.

Changes

Etherscan client (safe_eth/eth/clients/etherscan_client_v2.py):

  • Any message with rate limit, and HTTP 429, raise EtherscanRateLimitError
  • Other non ok HTTP responses raise the new EtherscanHttpError (with status_code)
  • Other API errors (e.g. Invalid API Key) raise EtherscanClientException
  • None is only returned when the contract is not verified
  • Transport errors are wrapped with wrap_http_exceptions as the new EtherscanConnectionError (also a builtin ConnectionError)
  • _retry_request raises EtherscanRateLimitError after the last attempt, instead of returning None
  • Same changes for AsyncEtherscanClientV2

Tests and CI:

  • Fix the flaky marker kwarg: reruns_delay
  • New network pytest marker for tests hitting external services: Etherscan, Blockscout, Sourcify, ENS, CowSwap, 4337 bundler, Transaction Service API, and the mainnet/polygon RPC node tests. CI runs them only on Python 3.13, the other jobs run -m "not network"
  • The markers are set per matrix entry in include:. A python-version == 3.13 && '' || 'not network' expression does not work: GitHub Actions treats '' as false, so it always returns 'not network'
  • Etherscan network tests skip on rate limit, connection and HTTP errors. An invalid API key still fails the CI
  • Offline unit tests for response processing, HTTP errors, retries and connection errors (sync and async)

Breaking change

Callers that relied on None for Etherscan errors now get an exception. All new exceptions subclass EtherscanClientException.

safe-decoder-service catches only (OSError, EtherscanRateLimitError) in its Etherscan -> Sourcify -> Blockscout fallback, so an HTTP error or invalid key would stop the fallback. It must be fixed before upgrading: PLA-2030.

@Uxio0
Uxío (Uxio0) requested a review from a team as a code owner September 23, 2026 12:31
@Uxio0 Uxío (Uxio0) added the breaking_change Breaking change label Sep 23, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T12:34:14.082904Z b72b8c2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Etherscan client:
- Detect every rate limit message (per second, daily, free tier) and HTTP 429
  as EtherscanRateLimitError. Before, only "Max rate limit reached" was
  detected and any other error returned None
- Raise EtherscanHttpError for other non ok HTTP responses and
  EtherscanClientException for other API errors, instead of returning None.
  None now only means the contract is not verified
- Wrap transport errors with wrap_http_exceptions as EtherscanConnectionError
- _retry_request raises EtherscanRateLimitError after the last attempt
  instead of returning None

Tests and CI:
- Fix pytest.mark.flaky kwarg: delay is not valid, reruns_delay is
- Add a `network` marker for tests hitting external services (third party
  APIs and RPC nodes). CI only runs them on Python 3.13, so the 4 matrix
  jobs do not share the same API keys at the same time
- Etherscan network tests skip on rate limit, connection and HTTP errors,
  but an invalid API key still fails
- Add offline unit tests for the Etherscan error handling
Comment thread safe_eth/eth/clients/etherscan_client_v2.py
Comment thread safe_eth/eth/clients/etherscan_client_v2.py Outdated
@falvaradorodriguez

Copy link
Copy Markdown
Contributor

There’s also an issue with the GitHub configuration. This is now pending:
Screenshot 2026-09-24 at 10 49 26

- Add EtherscanDailyRateLimitError, raised for the daily quota message.
  _retry_request raises it with no retry, as the quota only resets the next day
- Move the last attempt of _retry_request out of the loop, so there is no
  dead `return None`
- Name the test-app job `test-app (<python version>)`, so it matches the
  required status checks on main. With the `include:` matrix GitHub added
  `not network` to the job name
@Uxio0
Uxío (Uxio0) force-pushed the fix/etherscan-flaky-tests branch from b72b8c2 to 3456077 Compare September 24, 2026 10:45
test_domain_hash_to_hex_str and the mocked test_init make no request, so they run on every Python version.
@Uxio0
Uxío (Uxio0) merged commit 4b5309f into main Sep 24, 2026
11 checks passed
@Uxio0
Uxío (Uxio0) deleted the fix/etherscan-flaky-tests branch September 24, 2026 12:35
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

breaking_change Breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants