Hmbown/CodeWhale · error
Goal changed while preparing the turn; retry
Error message
Goal changed while preparing the turn; retry
What it means
While preparing the turn, the runtime re-reads the thread's goal under the goal-mutation lock and compares it to the goal captured at request time. If the goal changed concurrently, preparation is aborted with this retryable error to avoid running a turn against a goal the user no longer set.
Solutions
- Retry the turn submission; the error is explicitly a retry hint.
- Re-fetch the thread and its current goal, then resubmit with fresh state.
- Stop mutating the thread goal while turns are being submitted (or pause goal edits during runs).
Example fix
// before
match runtime.submit_turn(t, req) { Err(_) => panic!(), ... }
// after
match runtime.submit_turn(t, req) {
Err(e) if e.contains("retry") => runtime.submit_turn(t, refresh(req)).await?,
other => other?,
} Defensive patterns
Strategy: retry
Validate before calling
let goal_now = store.load_goal(thread_id)?;
if goal_now != turn_goal {
// resubmit with fresh state before calling the API
} Try / catch
match submit_turn(id, req).await {
Err(e) if e.to_string().contains("retry") => submit_turn(id, req_fresh()).await,
other => other,
} Prevention
- Lock goal edits while turns are being submitted.
- Refresh the thread snapshot immediately before each submission attempt.
- Treat 'retry' errors as expected and add bounded retry logic.
When it happens
Trigger: Another task/user updated the thread's stored goal (store.update_goal) between turn submission and the locked re-read inside turn preparation.
Common situations: User edits the thread goal while a turn is being submitted; an automation rewrites goals concurrently with a scheduled turn; retry storms after a first failed preparation.
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
- Runtime turn operation is already being claimed; retry
- Thread execution settings changed while preparing the turn…
- A saved trace identifier collided. Try saving again.
- a window change is already in progress
- another Codewhale process
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/0495eb3471d3eaf3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:10243
.transpose()?;
// A concurrent exact retry may have crossed the first lookup
// before the original request committed its binding. Recheck
// under the same claim lock before inspecting active-turn state or
// persisting/sending anything.
if let Some(operation) = operation.as_ref()
&& let Some(original_turn) = self.replay_turn_for_operation(operation)?
{
return Ok((original_turn, true));
}
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 _goal_mutation = self.store.goal_mutation.lock();
if self.store.load_goal(thread_id)? != turn_goal {
bail!("Goal changed while preparing the turn; retry");
}
engine.restore_runtime_goal(turn_goal.as_ref())?;
let _thread_mutation = self.store.thread_mutation.lock();
let mut current_thread = self.store.load_thread(thread_id)?;
if !thread_execution_state_matches(&thread, ¤t_thread) {
bail!("Thread execution settings changed while preparing the turn; retry");
}
let previous_active_route = (state.route_identity.clone(), state.route_model.clone());
state.active_turn = Some(ActiveTurnState {
goal_id: turn_goal.as_ref().map(|goal| goal.goal_id.clone()),
turn_id: turn_id.clone(),
interrupt_requested: false,
compaction_id: None,
});
state.route_identity = provider_identity;
state.route_model.clone_from(&model);
let persistence_result = (|| -> Result<()> {View on GitHub (pinned to 73e0f67d83)