Hmbown/CodeWhale · error

API timeout should publish an Interrupted mailbox lifecycle…

Error message

API timeout should publish an Interrupted mailbox lifecycle event

What it means

Panic from `.expect("API timeout should publish an Interrupted mailbox lifecycle event")`: the test's polling future completed without ever draining a MailboxMessage::Interrupted from mailbox_rx within 5 seconds. It asserts that an API timeout is surfaced to the parent as an Interrupted lifecycle event with an 'API call timed out' reason.

Solutions

  1. Check that the API-timeout branch in the sub-agent turn loop publishes MailboxMessage::Interrupted
  2. Confirm the reason string still contains "API call timed out"
  3. Verify mailbox_rx is subscribed/connected before the task starts so early events aren't lost
  4. Increase polling frequency or drain-before-timeout if events arrive after the 5s window

Example fix

// before
.await.expect("API timeout should publish an Interrupted mailbox lifecycle event");
// after
.await.unwrap_or_else(|| panic!("no Interrupted event within 5s; drained={:?}", seen_ids));
Defensive patterns

Strategy: try-catch

Type guard

fn find_interrupted(msgs: &[MailboxMessage]) -> Option<&MailboxMessage> {
    msgs.iter().find(|m| matches!(m, MailboxMessage::Interrupted { .. }))
}

Try / catch

match tokio::time::timeout(Duration::from_secs(5), poll_interrupted()).await {
    Ok(env) => env,
    Err(_) => panic!("Interrupted mailbox event never published"),
}

Prevention

When it happens

Trigger: The sub-agent times out on its API call but the Interrupted mailbox message is never published (or is published with a different variant/topic), or the drain loop misses it before the timeout elapses.

Common situations: Refactor of the mailbox event enum or publish path dropping the Interrupted emission on the timeout branch; reason string changed so the later contains("API call timed out") also fails; channel closed early.

Understand the failure class

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/8763007b2328e61c. Report an issue: GitHub.

Appendix: source

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

    .await
    .expect("first timed-out API attempt should reach the test server");

    let interrupted_envelope = tokio::time::timeout(Duration::from_secs(5), async {
        loop {
            for env in mailbox_rx.drain() {
                if let MailboxMessage::Interrupted {
                    agent_id: id,
                    reason,
                } = env.message
                {
                    return (id, reason);
                }
            }
            tokio::time::sleep(Duration::from_millis(10)).await;
        }
    })
    .await
    .expect("API timeout should publish an Interrupted mailbox lifecycle event");
    assert_eq!(interrupted_envelope.0, agent_id);
    assert!(
        interrupted_envelope.1.contains("API call timed out"),
        "reason should carry the timeout context: {}",
        interrupted_envelope.1
    );

    tokio::time::timeout(Duration::from_secs(5), task_handle)
        .await
        .expect("sub-agent task must not park waiting for checkpoint input")
        .expect("sub-agent task should finish");
    assert_eq!(
        calls.load(Ordering::SeqCst),
        SUBAGENT_API_TIMEOUT_MAX_RETRIES.saturating_add(1) as usize,
        "needs-input interruption must not park for continuation; the API call \
         is retried up to the timeout-retry budget, then stops"
    );

View on GitHub (pinned to 433685b202)