Hmbown/CodeWhale · warning
parent cancellation fan-in
Error message
parent cancellation fan-in
What it means
Test assertion `completion_rx.try_recv().expect("parent cancellation fan-in")` at `crates/tui/src/tools/subagent/tests.rs:6601`. The test expects that when a parent cancels a child agent, exactly one completion event is fanned into the completion channel. A panic here means no completion event was delivered at all (try_recv returned Empty/Disconnected) — the cancellation did not propagate to the completion fan-in path.
Solutions
- Replace try_recv with a bounded recv (with timeout) in a debug run to distinguish 'no event ever' from 'not yet delivered' (race).
- Trace the cancel_agent path and confirm it sends an AgentComplete with cancelled status into the completion channel exactly once.
- Check the parent-child cancellation token wiring: interrupt/cancel must trigger the agent's terminal transition that emits the event.
- If a dedup/fan-in guard was added recently, ensure it drops duplicates, not the first event.
Example fix
// before
let completion = completion_rx.try_recv().expect("parent cancellation fan-in");
// after
let completion = tokio::time::timeout(std::time::Duration::from_secs(5), completion_rx.recv())
.await
.expect("timed out waiting for parent cancellation fan-in")
.expect("completion channel closed before fan-in event"); Defensive patterns
Strategy: try-catch
Validate before calling
// Replace bare try_recv with a bounded await in tests: let completion = tokio::time::timeout(Duration::from_secs(5), completion_rx.recv()).await; assert!(matches!(completion, Ok(Some(_))), "expected exactly one fan-in completion");
Try / catch
let completion = timeout(Duration::from_secs(5), completion_rx.recv()).await
.expect("fan-in event never arrived (timeout)")
.expect("completion sender dropped before fan-in"); Prevention
- Never assert on channels with bare try_recv in async tests; use bounded recv.
- Keep the completion sender alive until the terminal transition completes.
- Guard fan-in dedup logic so it drops duplicates, never the first event.
When it happens
Trigger: The cancel path never emits the AgentComplete event: e.g. the cancellation token is not observed by the agent's wait loop, the completion sender was dropped before the cancel, or a refactor routes completion through a different channel/mailbox than the one this test drains.
Common situations: Changes to SubAgentManager's cancellation plumbing (token wiring, mailbox draining, fan-in dedup) that drop the terminal completion event; tests flaking if cancellation races event delivery — though try_recv after awaits should be deterministic if fan-in happens before return.
Related errors
- first Stop
- repeated Stop
- cancelled tool result is always model-visible
- first coordination interrupt
- missing
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1eb53d648f663465.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/tests.rs:6601
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()
.into_iter()
.filter(|envelope| {
matches!(
envelope.message,
MailboxMessage::Completed { .. }
| MailboxMessage::Failed { .. }
| MailboxMessage::Interrupted { .. }
| MailboxMessage::Cancelled { .. }
)
})
.collect::<Vec<_>>();
assert_eq!(terminal_mail.len(), 1);
assert!(matches!(View on GitHub (pinned to 73e0f67d83)