Hmbown/CodeWhale · error
first timed-out API attempt should reach the test server
Error message
first timed-out API attempt should reach the test server
What it means
Panic from `.expect("first timed-out API attempt should reach the test server")` on a `tokio::time::timeout` wrapping a polling loop. The test awaited the axum test server recording at least one API call within the window after a deliberately timed-out first attempt; if the loop ends without seeing the call, the retry/timeout machinery failed to re-issue the request.
Solutions
- Verify the retry path in SubAgentManager actually re-issues the API call after the client timeout
- Confirm the mock axum server increments the `calls` counter on each request
- Check that the timeout windows (client timeout vs test timeout) leave room for the retry to be observed
- Inspect test logs for the first attempt's timeout firing before the loop starts
Example fix
// before
.await.expect("first timed-out API attempt should reach the test server");
// after
.await.unwrap_or_else(|| panic!(
"no API attempt reached the test server after timeout; calls={}\",
calls.load(Ordering::SeqCst)")); Defensive patterns
Strategy: retry
Try / catch
tokio::time::timeout(Duration::from_secs(5), poll_for_call())
.await
.unwrap_or_else(|_| panic!("retry never reached test server; calls={}", calls.load(Ordering::SeqCst))); Prevention
- Keep client timeouts comfortably smaller than test await windows
- Log each mock-server hit so retries are observable in failures
- Assert the retry count alongside reachability to distinguish no-retry from slow-retry
When it happens
Trigger: Running api_timeout_preserves_checkpoint_and_returns_needs_input_without_parking when the sub-agent's retry path does not re-send the API request after the first attempt times out, or the mock server's request counter never increments within the awaited window.
Common situations: A regression in SUBAGENT_API_TIMEOUT retry logic (retries skipped or retried against the wrong client), mock server bound to a different port, or the polling loop checking the wrong counter.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- API timeout should publish an Interrupted mailbox lifecycle…
- child wall-time budget exhausted
- sub-agent task must not park waiting for checkpoint input
- sub-agent task should finish
- Agent not found
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/0d793aa81fbdd241.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/tests.rs:8988
max_steps: 3,
token_budget: None,
wall_time: DEFAULT_CHILD_WALL_TIME,
input_rx: task_input_rx,
launch_gate: None,
_foreground_child_registration: None,
};
let task_handle = tokio::spawn(run_subagent_task(task));
tokio::time::timeout(Duration::from_secs(5), async {
loop {
if calls.load(Ordering::SeqCst) >= 1 {
break;
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await
.expect("first timed-out API attempt should reach the test server");
let interrupted_envelope = tokio::time::timeout(Duration::from_secs(5), async {
loop {
for env in mailbox_rx.drain() {
if let MailboxMessage::Interrupted {
agent_id: id,
reason,
} = env.message
{
return (id, reason);
}
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await
.expect("API timeout should publish an Interrupted mailbox lifecycle event");
assert_eq!(interrupted_envelope.0, agent_id);View on GitHub (pinned to 433685b202)