Hmbown/CodeWhale · warning
Thread execution settings changed while preparing…
Error message
Thread execution settings changed while preparing compaction; retry
What it means
Thrown by the compaction preparation step when the persisted thread record no longer matches the in-memory thread snapshot used to start compaction (`thread_execution_state_matches` fails). Execution-relevant settings (route, model, or other execution state) changed between the caller's read and the mutation-locked re-load, so proceeding would compact under the wrong configuration.
Solutions
- Retry compaction: re-fetch the thread with the updated settings and call compact_thread again — the message explicitly says 'retry'.
- Avoid changing thread execution settings (model/route) while compaction or a turn is being prepared.
- Serialize settings changes and compaction through the store's thread_mutation lock or a single settings API so they cannot interleave.
Example fix
// before
runtime.compact_thread(thread_id).await?;
// after
match runtime.compact_thread(thread_id).await {
Err(e) if e.to_string().contains("execution settings changed") => {
// settings moved: re-read the thread and retry once
runtime.compact_thread(thread_id).await?
}
other => other?,
} Defensive patterns
Strategy: retry
Validate before calling
// Re-read the thread right before compacting so the snapshot is fresh let thread = runtime.get_thread(thread_id).await?; let _engine = runtime.ensure_engine_loaded(&thread).await?; runtime.compact_thread(thread_id).await?;
Type guard
fn settings_match(snapshot: &Thread, persisted: &Thread) -> bool {
snapshot.execution_state() == persisted.execution_state()
} Try / catch
const MAX_RETRIES: usize = 2;
for _ in 0..MAX_RETRIES {
match runtime.compact_thread(thread_id).await {
Err(e) if e.to_string().contains("execution settings changed") => continue,
other => break other,
}
} Prevention
- Do not change thread model/route settings while compaction or turn preparation is in flight.
- Apply execution-setting changes through a single serialized API path.
- Retry compaction once on this error before surfacing it; the mismatch is usually transient.
When it happens
Trigger: Calling `compact_thread` while another actor changes the thread's execution settings (model/route switch, config update, thread edit) between the initial `get_thread`/route resolution and the `store.load_thread` inside the mutation lock — the mismatch check fails.
Common situations: User switches model while auto-compaction is preparing; a concurrent settings update lands mid-compaction-setup; race between two UI actions (edit thread + compact) on the same thread.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Thread engine changed while preparing steer; retry
- Compaction failed after
- Turn is no longer in progress and cannot be steered
- Turn moved on before the steer reached the model;
- a window change is already in progress
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a4e42789aac49fc8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:10623
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, ¤t_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,
turn_id: turn_id.clone(),
interrupt_requested: false,
compaction_id: Some(compaction_id),
});
state.route_identity = route_identity;
state.route_model = route_model;
let persistence_result = (|| -> Result<()> {
self.store.save_turn(&turn)?;
current_thread.latest_turn_id = Some(turn_id.clone());
current_thread.updated_at = now;
self.store.save_thread(¤t_thread)
})();
if let Err(persistence_error) = persistence_result {View on GitHub (pinned to 73e0f67d83)