Hmbown/CodeWhale · error
sub-agent join should succeed
Error message
sub-agent join should succeed
What it means
Panic from the second `.expect("sub-agent join should succeed")` at crates/tui/src/tools/subagent/tests.rs:9155: the outer timeout completed (the JoinHandle resolved), but joining the spawned `run_subagent_task` task returned a `JoinError`, meaning the spawned task itself panicked. The inner task's original panic message is the real diagnostic; this expect only reports the join failure.
Solutions
- Read the panic printed above this message in the test output — JoinError's cause carries the inner task's panic message and backtrace (`RUST_BACKTRACE=1`).
- Inspect `run_subagent_task` for unwrap/expect on the retry path; run it under the delayed_chat_client stub in isolation.
- Check that `task_input_tx` is kept alive for the task's lifetime (it is dropped only after the test completes here).
- If the inner panic is a timeout-related unwrap, verify the step API timeout (50ms) vs. the mock's 150ms first response still matches the test's intent.
Example fix
// before
.await
.expect("sub-agent task should finish")
.expect("sub-agent join should succeed");
// after
.await
.expect("sub-agent task should finish")
.unwrap_or_else(|e| panic!("sub-agent task panicked: {e}")); Defensive patterns
Strategy: try-catch
Try / catch
let handle = tokio::spawn(run_subagent_task(task));
match tokio::time::timeout(Duration::from_secs(10), handle).await {
Ok(Ok(())) => {}
Ok(Err(join)) => panic!("sub-agent task panicked: {join}"),
Err(_) => panic!("sub-agent task timed out"),
} Prevention
- Replace bare .expect on JoinHandle results with a message that includes the JoinError so the inner panic is surfaced.
- Run flaky task tests with RUST_BACKTRACE=1 and --nocapture in CI logs.
- Keep channel senders (task_input_tx) alive for the spawned task's lifetime.
- Avoid unwrap/expect inside production task code paths exercised by tests.
When it happens
Trigger: Inside `run_subagent_task` for `subagent_retries_api_timeout_before_succeeding`: any panic during the 150ms-timeout → retry → complete flow, e.g. an unwrap on a channel send, a snapshot assertion, or an unwrap on manager state in the production task code.
Common situations: A regression in the sub-agent retry loop (crates/tui/src/tools/subagent) that unwraps a None/Err on the recovered attempt; channel closed because task_input_tx was dropped while the task expects input; an assertion or expect inside the spawned task firing under the 50ms step timeout.
Related errors
- API timeout should publish an Interrupted mailbox lifecycle…
- ApplyPatchPreflight should serialize
- bing result regex pattern is valid
- bing snippet regex pattern is valid
- bing title regex pattern is valid
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/82f3a366365b0315.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/tests.rs:9155
assignment: make_assignment(),
allowed_tools: Some(vec![]),
fork_context: false,
started_at: Instant::now(),
max_steps: 3,
token_budget: None,
wall_time: DEFAULT_CHILD_WALL_TIME,
input_rx: task_input_rx,
launch_gate: None,
_foreground_child_registration: None,
};
tokio::time::timeout(
Duration::from_secs(10),
tokio::spawn(run_subagent_task(task)),
)
.await
.expect("sub-agent task should finish")
.expect("sub-agent join should succeed");
assert_eq!(
calls.load(Ordering::SeqCst),
2,
"one timed-out API attempt should be retried exactly once"
);
let snapshot = {
let manager = manager.read().await;
manager
.get_result(&agent_id)
.expect("agent should stay registered")
};
assert_eq!(snapshot.status, SubAgentStatus::Completed);
assert_eq!(snapshot.result.as_deref(), Some("recovered answer"));
}
#[test]
fn api_timeout_retry_backoff_doubles_and_caps() {View on GitHub (pinned to 433685b202)