Hmbown/CodeWhale · error
Failed to steer turn
Error message
Failed to steer turn: {error} What it means
Raised when `engine.reserve_steer()` fails while attempting to steer (inject a mid-turn user message into) an active turn. The engine's steer mailbox could not reserve a slot — typically because the turn is no longer accepting steers (finishing/stopping) or the engine's operation channel is saturated or closed. The underlying engine error is interpolated into the message.
Solutions
- Retry the steer; if the turn truly finished, send a normal new message instead.
- Check whether the turn was interrupted or completed concurrently (`Turn is stopping and cannot be steered` guards run first).
- If the engine channel is closed, reload the thread (ensure the engine is re-spawned) and retry.
Defensive patterns
Strategy: fallback
Try / catch
if let Err(e) = runtime.steer_turn(&thread_id, &turn_id, text).await {
// fall back to sending a new message on the thread
} Prevention
- Steer early in the turn rather than right as it finishes.
- Check turn status before steering; do not steer interrupted/stopping turns.
- Avoid steering while a config reload or engine swap is in progress.
- Treat a steer failure as a signal to check whether the turn already completed.
When it happens
Trigger: Calling the steer API (`steer_turn`) on a running turn when `reserve_steer()` returns Err — e.g. the active turn just reached its terminal phase, an interrupt was requested concurrently, or the engine op channel is closed/full.
Common situations: User sends a steer message at the same moment the model finishes its turn; steering a turn that another window just interrupted; racing an engine reload (the channel changed between locks).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Steer receipt task ended before acknowledgement
- catalog cache unavailable
- child-local search
- child wall-time budget exhausted
- {}
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/364a3f0f8ea5d1eb.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:10412
let Some(active_turn) = active_thread.active_turn.as_mut() else {
bail!("No active turn on thread {thread_id}");
};
if active_turn.turn_id != turn_id {
bail!("Turn {turn_id} is not active on thread {thread_id}");
}
if active_turn.interrupt_requested {
bail!("Turn {turn_id} is stopping and cannot be steered");
}
active_thread.engine.clone()
};
touch_lru(&mut active.lru, thread_id);
engine
};
let permit = engine
.reserve_steer()
.await
.map_err(|error| anyhow!("Failed to steer turn: {error}"))?;
let now = Utc::now();
let queued_turn;
let item = TurnItemRecord {
schema_version: CURRENT_RUNTIME_SCHEMA_VERSION,
id: format!("item_{}", &Uuid::new_v4().to_string()[..8]),
turn_id: turn_id.to_string(),
kind: TurnItemKind::UserMessage,
// Queued, not Completed: the text is in the engine's mailbox, not
// yet in the turn's record. `spawn_steer_settlement` flips it once
// the engine reports what actually happened (#6276).
status: TurnItemLifecycleStatus::Queued,
summary: summarize_text(&prompt, SUMMARY_LIMIT),
detail: Some(prompt.clone()),
metadata: None,
artifact_refs: Vec::new(),
started_at: Some(now),
ended_at: None,View on GitHub (pinned to 73e0f67d83)