Hmbown/CodeWhale · warning

repeated Stop

Error message

repeated Stop

What it means

Test assertion `manager.cancel_agent(&agent_id).expect("repeated Stop")` at `crates/tui/src/tools/subagent/tests.rs:6588`, immediately following the first cancel. It verifies that issuing Stop a second time on an already-cancelled agent is idempotent and still succeeds (returning a record whose status is Cancelled). A panic means the second cancel returned an error instead of being a no-op success.

Solutions

  1. Inspect the Err from the second cancel_agent call (unwrap_or_else with the error in the panic message).
  2. Ensure cancel_agent returns the existing Cancelled record for an already-terminal agent instead of an Err.
  3. Verify the worker record is retained after the first cancel so the second call can find it.
  4. Add/keep a regression test pinning cancel idempotency if it was removed.

Example fix

// before
let second = manager.cancel_agent(&agent_id).expect("repeated Stop");
// after
let second = manager.cancel_agent(&agent_id)
    .unwrap_or_else(|e| panic!("repeated Stop must be idempotent, got: {e:?}"));
Defensive patterns

Strategy: type-guard

Validate before calling

// After the first cancel, confirm the record still exists before re-cancelling:
assert!(manager.get_worker_record(&agent_id).is_some(), "record must survive first cancel");

Try / catch

let second = manager.cancel_agent(&agent_id).unwrap_or_else(|e| panic!("repeated Stop must be idempotent: {e:?}"));

Prevention

When it happens

Trigger: Calling cancel_agent twice when the manager treats an already-cancelled agent as an error (e.g. 'agent not in cancellable state') rather than returning the existing record; or record eviction after the first cancel leaving the lookup to fail.

Common situations: A refactor that adds strict state-transition validation to cancel_agent (rejecting transitions from terminal states) breaking the documented idempotency contract; or the record-removal bug shared with the 'worker record remains inspectable' assertion in the same suite.

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/0e5fa64d4bb039bd. Report an issue: GitHub.

Appendix: source

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

    let (completion_tx, mut completion_rx) = mpsc::channel::<SubAgentCompletion>(16);
    let (mailbox, mut mailbox_rx) = Mailbox::new(CancellationToken::new());
    let (event_tx, mut event_rx) = mpsc::channel(8);
    let mut runtime = runtime_with_depth(1, Some(completion_tx));
    runtime.mailbox = Some(mailbox);
    runtime.event_tx = Some(event_tx);
    agent.terminal_delivery = Some(SubAgentTerminalDeliveryContext::from_runtime(&runtime));
    manager.agents.insert(agent_id.clone(), agent);
    manager.register_worker(make_worker_spec(&agent_id, tmp.path().to_path_buf()));
    manager.record_worker_event(
        &agent_id,
        AgentWorkerStatus::ModelWait,
        Some(SUBAGENT_MODEL_WAIT_REASON.to_string()),
        Some(1),
        None,
    );

    let first = manager.cancel_agent(&agent_id).expect("first Stop");
    let second = manager.cancel_agent(&agent_id).expect("repeated Stop");
    assert_eq!(first.status, SubAgentStatus::Cancelled);
    assert_eq!(second.status, SubAgentStatus::Cancelled);
    assert_eq!(
        first
            .checkpoint
            .as_ref()
            .map(|checkpoint| checkpoint.reason.as_str()),
        Some("test_checkpoint")
    );

    let completion = completion_rx
        .try_recv()
        .expect("parent cancellation fan-in");
    assert!(completion.payload.contains(r#""status":"cancelled""#));
    assert!(completion_rx.try_recv().is_err());

    let terminal_mail = mailbox_rx
        .drain()

View on GitHub (pinned to 73e0f67d83)