Repository navigation
fix(cli): say why a request got no response, and what to check - #90
Conversation
1684850 to
134f858
Compare
tee8z
left a comment
There was a problem hiding this comment.
Requesting changes on 134f858. The classification is right. The rustls check walks the nested io::Errors, a connect timeout is checked before a timeout, a write that may have been applied is never told to retry, and the raw error text stays out of the message. One finding:
P2: an auth-service timeout blames --timeout, which does not apply to it. The auth client is client(AUTH_TIMEOUT), a fixed 30 seconds (src/auth.rs:30, :227). --timeout reaches only the API client (api.rs:186) and the price service. Yet TransportFailure::Timeout always says no response within --timeout, and for a read its hint is Retry, or pass a larger --timeout. I reproduced this on this head against a silent auth server:
$ voltage login --no-browser --json --auth-url http://127.0.0.1:PORT --timeout 300
{"error":{"exit_code":4,"hint":"Retry, or pass a larger --timeout.","message":"Authentication request failed: no response within --timeout; no token request was automatically retried"}}
It exited after 30 seconds, not 300. An agent that follows the hint raises --timeout and gets the same failure. This PR already gives ConnectTimeout its own text for this reason. Let the caller say which limit applied, for example by passing the limit's description to no_response or a flag for a fixed limit, so that auth reports "within 30 seconds" and does not suggest --timeout. Please add a test for it.
Nonblocking: the description's table lists four classes and has no row for ConnectTimeout. The CHANGELOG and troubleshooting guide do describe it.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (137 unit, 122 integration) and python3 scripts/check-coverage.py passed. CRAP: 623 measured functions, none at or above 20, highest wait_for_payment at 19.60; 176 functions were unmeasured. CI is green.
134f858 to
e4f73a8
Compare
tee8z
left a comment
There was a problem hiding this comment.
Approved e4f73a8 against fix/cli-output-file-exists (#88 78c8a98). My finding is fixed. ResponseLimit::Fixed(AUTH_TIMEOUT) is passed only from auth_request. The API, the checkout stream and the price service, whose clients are built from global.timeout, keep ResponseLimit::Timeout. A write that times out against a fixed limit still leads with the look-up-first step.
I repeated my reproduction on this head. voltage login --no-browser --json --timeout 300 against a silent auth server now exits after 30 seconds with Authentication request failed: no response within 30 seconds; no token request was automatically retried and the hint Retry. The auth service has a fixed 30-second limit, which --timeout does not change. The new unit test pins both.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (139 unit, 122 integration) and python3 scripts/check-coverage.py passed. CRAP: 625 measured functions, none at or above 20, highest wait_for_payment at 19.60; 176 functions were unmeasured. When I approved, CI on x86_64 macOS was still pending, and the other jobs had passed.
A request that gets no HTTP response now says why it failed, and a hint names the next step. Before, every failure read the same way, for example "HTTP read request failed. Check the API URL and whether the API service is running." The underlying error was dropped.
reqwest::Error::is_timeoutno response within --timeout; for the auth service,no response within 30 seconds--timeout. For the auth service: retry, and its fixed 30-second limit is one that--timeoutdoes not change.is_timeoutandis_connectcould not connect within 10 seconds--timeoutdoes not change this connect limit.rustls::Erroranywhere in the cause chain, including inside nestedio::Errorsthe TLS connection failed, for example because the server's certificate was not trustedis_connect: DNS, a refused port or a proxycould not connectthe connection closed before a response arrivedThis covers the API (reads, writes and the checkout event stream), the auth service and the price service. Each caller says which response limit applied: the API and price service use
--timeout, and the auth service uses its fixed 30 seconds (AUTH_TIMEOUT). Each keeps its own consequence text: a write still says "a write may have been submitted. No mutation was retried." and keeps its unknown-outcomeerror.detail. The raw error text still never reaches the output, because it can contain hosts and paths.Why: in the CLI benchmark, a sandbox blocked macOS
trustd, so every TLS check failed in 19 of 23 CLI runs. The CLI said only "HTTP read request failed" while curl kept working, so agents switched to curl. Closes #84.Stack: 3 of 6, on #88. It doesn't depend on #87 or #88.
CHANGELOG.md(Unreleased) line is added, anddocs/guides/troubleshooting.md(Exit code 4) describes the classes.cargo test(139 unit, 122 integration) andcheck-coverage.py.auth_requestagainst a silent server and checks that the timeout names the fixed 30 seconds and does not suggest--timeout. Manual check:login --no-browser --json --timeout 300against a silent auth server exits 4 after 30 seconds with that message. An integration test runs each of the 5 call sites against a closed port and checks exit code 4, the message and the hint, and that a write keepsdetail.outcome = unknown.e4f73a8: 625 functions measured, 0 at or above 20. The highest iswait_for_paymentat 19.60. The new functions score:auth_request6.0,TransportFailure::of,classify,whatandnext_step2.0 each,no_response1.0.rustls::Error). The DNS case is grouped under Connect and isn't tested on its own, because it would need a resolver.