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

  1. Retry the turn submission; the error is explicitly a retry hint.
  2. Re-fetch the thread and its current goal, then resubmit with fresh state.
  3. 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

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


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, &current_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)