Hmbown/CodeWhale · warning

Runtime terminal receipt monitor failed

Error message

Runtime terminal receipt monitor failed: {error}

What it means

During RuntimeThreadManager shutdown, terminal receipt monitors are awaited with await_retained_completion. If any turn monitor resolves with an error, shutdown records 'Runtime terminal receipt monitor failed'. Receipt monitors are background tasks that publish terminal receipts when a turn finishes, so this indicates a monitor task ended abnormally.

Solutions

  1. Check the wrapped {error} to identify which monitor failed and why
  2. Ensure turn completion paths publish their receipts successfully before shutdown
  3. Verify the turn store is accessible so receipt writes succeed
  4. Call shutdown again or drain remaining monitors if the loop broke out with monitors still present

Example fix

// before
let _ = manager.shutdown().await;
// after
match manager.shutdown().await {
    Ok(()) => {}
    Err(e) if e.to_string().contains("receipt monitor") => {
        tracing::warn!("receipt monitor failed during shutdown: {e:#}");
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

let monitors = manager.turn_monitors.lock().clone();
let monitors_done = monitors.iter().all(completion_finished);

Type guard

fn monitor_finished(monitor: &RuntimeCompletion) -> bool { completion_finished(monitor) }

Try / catch

match manager.shutdown().await {
    Err(e) if e.to_string().contains("receipt monitor") => {
        tracing::warn!("monitor failure at shutdown (turn receipts may be missing): {e:#}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling RuntimeThreadManager::shutdown while turn monitors are registered; a monitor's retained completion returns Err (monitor task failed or was cancelled), and the retain loop that drops finished monitors then finds remaining failures.

Common situations: Tearing down the runtime with turns whose receipt monitors never completed; a monitor task panicking while persisting a receipt; store write failures during receipt publication.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:5230

        {
            let _ = engine.send(Op::Shutdown).await;
        }
        for (_, worker) in workers {
            if let Err(error) = await_retained_completion(&worker).await {
                failure = Some(anyhow!("Runtime engine shutdown failed: {error}"));
            }
        }
        // Recovery reads can publish receipts too. Flushes that already passed
        // their admission check retain this mutex through all receipt writes;
        // later readers see the closed latch under the same mutex.
        {
            let _recovery = self.recovery_flush.lock().await;
        }
        loop {
            let monitors = self.turn_monitors.lock().clone();
            for monitor in monitors {
                if let Err(error) = await_retained_completion(&monitor).await {
                    failure = Some(anyhow!("Runtime terminal receipt monitor failed: {error}"));
                }
            }
            let mut monitors = self.turn_monitors.lock();
            monitors.retain(|monitor| !completion_finished(monitor));
            if monitors.is_empty() {
                break;
            }
        }
        self.engine_workers
            .lock()
            .retain(|(_, worker)| !completion_finished(worker));
        let mut active = self.active.lock().await;
        active.engines.clear();
        active.lru.clear();
        failure.map_or(Ok(()), Err)
    }

    fn track_receipt_worker(&self, worker: tokio::task::JoinHandle<()>) {

View on GitHub (pinned to 73e0f67d83)