Hmbown/CodeWhale · error

cancel wins race

Error message

cancel wins race

What it means

Test panic: `manager.cancel_agent(&agent_id)` returned Err, failing `.expect("cancel wins race")`. The cancel path must win races against late completions, so an Err means the manager refused or failed to cancel a registered, running worker.

Solutions

  1. Confirm the agent's status allows cancellation at the call site (not already terminal).
  2. Check `input_tx` is still open if cancel signals via the input channel.
  3. Read the Err payload to distinguish unknown-agent from state-conflict refusals.

Example fix

// before
manager.cancel_agent(&agent_id).expect("cancel wins race");
// after
manager.cancel_agent(&agent_id)
    .unwrap_or_else(|e| panic!("cancel wins race: {e:#}"));
Defensive patterns

Strategy: try-catch

Validate before calling

let cancelable = manager.agents.get(&agent_id)
    .map(|a| !matches!(a.status, SubAgentStatus::Cancelled | SubAgentStatus::Completed))
    .unwrap_or(false);
assert!(cancelable, "agent must exist and be non-terminal to cancel");

Try / catch

manager.cancel_agent(&agent_id)
    .unwrap_or_else(|e| panic!("cancel failed for {agent_id}: {e:#}"));

Prevention

When it happens

Trigger: Calling `cancel_agent` on an agent inserted into `manager.agents` with a registered worker spec, when the cancel path requires a state (e.g. Running) the agent does not have, or its input channel is closed.

Common situations: Regression where cancel requires a live input_tx sender but the test's receiver was dropped; terminal-state guard rejecting an agent already in a terminal status.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ac16a914698d3ed0. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/subagent/tests.rs:6795

    let mut manager = SubAgentManager::new(tmp.path().to_path_buf(), 2);
    let agent_id = "agent_cancel_completion_race".to_string();
    let (input_tx, _input_rx) = mpsc::unbounded_channel();
    let agent = SubAgent::new(
        agent_id.clone(),
        FleetRole::Worker,
        "race".to_string(),
        make_assignment(),
        "deepseek-v4-flash".to_string(),
        None,
        None,
        input_tx,
        tmp.path().to_path_buf(),
        manager.current_session_boot_id.clone(),
    );
    manager.agents.insert(agent_id.clone(), agent);
    manager.register_worker(make_worker_spec(&agent_id, tmp.path().to_path_buf()));

    manager.cancel_agent(&agent_id).expect("cancel wins race");
    let mut late = manager
        .get_result(&agent_id)
        .expect("cancelled snapshot exists");
    late.status = SubAgentStatus::Completed;
    late.result = Some("late success".to_string());
    assert!(
        !manager.update_from_result(&agent_id, late),
        "late completion must lose the terminal transition"
    );

    let snapshot = manager
        .get_result(&agent_id)
        .expect("terminal snapshot remains");
    assert_eq!(snapshot.status, SubAgentStatus::Cancelled);
    assert_eq!(
        snapshot.result.as_deref(),
        Some("Cancelled by parent request.")
    );

View on GitHub (pinned to 73e0f67d83)