Hmbown/CodeWhale · error

Failed to trigger compaction: engine operation channel…

Error message

Failed to trigger compaction: engine operation channel closed

What it means

Raised when reserving a slot on the engine's operation channel (`tx_op.reserve_owned()`) fails while triggering a compaction, which can only happen if the channel is closed — i.e. the engine task has already terminated. Without a reserved permit the runtime cannot hand the Op::CompactContext operation to the engine, so it fails fast with this error instead of hanging.

Solutions

  1. Retry compaction — `ensure_engine_loaded` will respawn a fresh engine if the old one died.
  2. Check logs for an engine task panic and address its root cause.
  3. Avoid triggering compaction during application shutdown.
  4. If it reproduces on one thread, unload/reload that thread to rebuild its engine.
Defensive patterns

Strategy: retry

Try / catch

match runtime.compact_thread(&thread_id, req).await {
    Err(e) if e.to_string().contains("operation channel closed") => {
        // engine died: reload thread / respawn engine, then retry compaction once
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling `compact_thread` when the thread's engine worker task has exited (panicked, aborted during shutdown, or completed) so `engine.tx_op` is closed and `reserve_owned().await` returns Err.

Common situations: The engine worker crashed earlier (check logs for panics); the app is shutting down while compaction is requested; the thread's engine was reaped (LRU eviction/cleanup) between `ensure_engine_loaded` and the reserve.

Related errors


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

Appendix: source

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

                &route_model,
            )),
            routed_usage: Vec::new(),
            routed_usage_drop_records: Vec::new(),
            routed_usage_source_ids: Vec::new(),
            routed_usage_dropped_records: 0,
            model_request_diagnostics: None,
            error: None,
            item_ids: Vec::new(),
            steer_count: 0,
            agent_mail_message_id: None,
        };
        let op = Op::CompactContext {
            id: compaction_id.clone(),
            route: Box::new(route),
            compaction: Box::new(compaction),
        };
        let permit = engine.tx_op.clone().reserve_owned().await.map_err(|_| {
            anyhow!("Failed to trigger compaction: engine operation channel closed")
        })?;

        let acceptance_rx = {
            let mut active = self.active.lock().await;
            let Some(state) = active.engines.get_mut(thread_id) else {
                bail!("Thread engine not loaded");
            };
            if state.active_turn.is_some() {
                bail!("Thread already has an active turn");
            }
            let _thread_mutation = self.store.thread_mutation.lock();
            let mut current_thread = self.store.load_thread(thread_id)?;
            if !thread_execution_state_matches(&thread, &current_thread) {
                bail!("Thread execution settings changed while preparing compaction; retry");
            }
            let previous_active_route = (state.route_identity.clone(), state.route_model.clone());
            state.active_turn = Some(ActiveTurnState {
                goal_id: None,

View on GitHub (pinned to 73e0f67d83)