Hmbown/CodeWhale · error
no engine installed for
Error message
no engine installed for {thread_id} What it means
Setting a turn's active state requires an engine already installed for the thread in the RuntimeThreadManager's active map. If active.engines has no entry for thread_id, the lookup fails with anyhow!("no engine installed for {thread_id}"). It is an internal sequencing error: the engine must be spawned/registered for a thread before turn operations target it.
Solutions
- Ensure the engine for thread_id is installed (spawned via the manager) before starting or interrupting turns
- Check earlier logs for a swallowed engine-installation failure for this thread
- Re-create/reinstall the thread's engine if it was torn down
- Validate the thread_id — it may come from a stale or foreign session
Example fix
// before
manager.set_active_turn(&thread_id, Some(&turn_id)).await?; // engine may not exist
// after
if manager.has_engine(&thread_id).await {
manager.set_active_turn(&thread_id, Some(&turn_id)).await?;
} else {
manager.install_engine(&thread_id).await?;
manager.set_active_turn(&thread_id, Some(&turn_id)).await?;
} Defensive patterns
Strategy: type-guard
Validate before calling
let has_engine = manager.active.lock().await.engines.contains_key(thread_id);
if !has_engine { bail!("engine not installed for {thread_id}; install it first"); } Type guard
async fn engine_installed(manager: &RuntimeThreadManager, thread_id: &str) -> bool {
manager.active.lock().await.engines.contains_key(thread_id)
} Try / catch
match manager.set_active_turn(thread_id, turn).await {
Err(e) if e.to_string().starts_with("no engine installed") => {
manager.install_engine(thread_id).await?;
manager.set_active_turn(thread_id, turn).await?;
}
other => other?,
} Prevention
- Always install the engine for a thread before any turn op
- Do not hold stale thread_ids across sessions; re-derive from the store
- Route turn ops through the manager so missing-engine errors surface once at install time
When it happens
Trigger: Calling the turn-state setter (active_turn update) with a thread_id that has no engine in active.engines — e.g. referencing a thread before its engine was installed, after the engine was removed/cleaned up, or after a shutdown removed engines.
Common situations: A stale turn_id/thread_id from a previous session being replayed; racing shutdown while a turn op is still queued; a bug where engine installation failed earlier and the error was swallowed.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Agent not found
- Agent Mail can be canceled only while queued
- Cannot queue a parent message for agent
- Cloud agent sandbox entered state
- Cloud job is and cannot be run; confirm it first with…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/7f6f5497db4e5a0f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:8193
);
}
}
Ok(())
}
/// Test seam: mark or clear an active turn on an installed test engine so
/// route-level tests can exercise restore admission.
#[cfg(test)]
pub(crate) async fn set_active_turn_for_test(
&self,
thread_id: &str,
turn_id: Option<&str>,
) -> Result<()> {
let mut active = self.active.lock().await;
let state = active
.engines
.get_mut(thread_id)
.ok_or_else(|| anyhow!("no engine installed for {thread_id}"))?;
state.active_turn = turn_id.map(|turn_id| ActiveTurnState {
turn_id: turn_id.to_string(),
goal_id: None,
interrupt_requested: false,
compaction_id: None,
});
Ok(())
}
/// Bind full-fidelity saved history to the exact prefix it covers. Callers
/// hold session_checkpoint_guard across the snapshot, save and this write.
pub(crate) async fn set_thread_session_checkpoint(
&self,
thread_id: &str,
session: &crate::session_manager::SavedSession,
) -> Result<()> {
let session_id = &session.metadata.id;
let messages_sha256 = session_messages_sha256(&session.messages)?;View on GitHub (pinned to 73e0f67d83)