Skip to content

fix(cow-ext): map reqwest timeout and 429 to Fault::Timeout / RateLimited#357

Closed
lgahdl wants to merge 2 commits into
nullislabs:developfrom
bleu:fix/287-cow-ext-rate-limit-timeout-fault
Closed

fix(cow-ext): map reqwest timeout and 429 to Fault::Timeout / RateLimited#357
lgahdl wants to merge 2 commits into
nullislabs:developfrom
bleu:fix/287-cow-ext-rate-limit-timeout-fault

Conversation

@lgahdl

@lgahdl lgahdl commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #287. ext_cow.rs's cow_error_to_wit mapped every reqwest::Error onto the blanket Fault::Unavailable, and every non-2xx orderbook status (including 429) onto the generic Http case — hiding two signals a caller's retry/backoff logic could otherwise act on precisely.

  • A 429 (CowApiError::HttpError { status: 429, .. }) now becomes Fault::RateLimited(RateLimit { retry_after_ms: None }) instead of Http(HttpFailure).
  • A genuine reqwest timeout (CowApiError::Network(e) where e.is_timeout()) now becomes Fault::Timeout instead of Fault::Unavailable.

This mirrors the chain-host's existing transport_fault classification (crates/nexum-runtime/src/host/error.rs), which already does the same 429→RateLimited / timeout→Timeout split for the chain-RPC side (#269's title calls this pattern out for that side).

retry_after_ms stays None — extracting a real value would require threading the response's Retry-After header through CowApiError::HttpError, which is a distinct, currently-blocked follow-up (#269 covers populating it generally).

Test plan

  • cargo test -p shepherd-cow-host ext_cow:: — added backend_http_429_projects_to_rate_limited_fault and backend_network_timeout_projects_to_timeout_fault (the latter drives a real reqwest::Client against a non-routable address with a 1ms deadline to get a genuine timeout error), both pass alongside the 4 existing tests
  • cargo clippy -p shepherd-cow-host --all-targets --all-features --locked -- -D warnings clean
  • cargo fmt -p shepherd-cow-host -- --check clean

…ited

ext_cow.rs projected every reqwest::Error onto the blanket
Fault::Unavailable and every non-2xx status (including 429) onto the
generic Http case, hiding backpressure and slow-request signals a
caller's retry logic could otherwise act on precisely. Mirrors the
chain-host's transport_fault classification (crates/nexum-runtime/src/
host/error.rs): a 429 now becomes Fault::RateLimited, and a genuine
reqwest timeout becomes Fault::Timeout. retry_after_ms stays None -
extracting it from the response would need a Retry-After header
threaded through CowApiError::HttpError, tracked separately by nullislabs#269.

Fixes nullislabs#287.
@lgahdl lgahdl requested a review from mfw78 as a code owner July 15, 2026 19:12
Relying on a non-routable IP (10.255.255.1) to force a reqwest
timeout is CI-runner-dependent: some network stacks fail the connect
attempt instantly with "no route to host" rather than hanging until
the deadline, which is a different reqwest error kind than a timeout.
Accept the connection but never respond instead, so the client's
deadline fires on a genuine read-timeout regardless of environment.
@lgahdl

lgahdl commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

by @mfw78
Superseded: the legacy cow-api extension it targets is removed by #293; the timeout/429 typed-fault requirement is carried by the cow adapter errorType-to-venue-error projection and the host-chain equivalent #269.

@lgahdl lgahdl closed this Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cow-ext: map reqwest timeout and 429 to Fault::Timeout / Fault::RateLimited

1 participant