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

  1. 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`).
  2. Inspect `run_subagent_task` for unwrap/expect on the retry path; run it under the delayed_chat_client stub in isolation.
  3. Check that `task_input_tx` is kept alive for the task's lifetime (it is dropped only after the test completes here).
  4. 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

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


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)