Hmbown/CodeWhale · error
translation route changed after turn dispatch; refusing to…
Error message
translation route changed after turn dispatch; refusing to reuse a different provider client
What it means
Before reusing a provider client for a translation request, `exact_translation_client` validates the freshly resolved route against the route recorded at turn dispatch time: provider identity key, model, and endpoint base_url must all match exactly. Any drift means the code refuses to send credentials/completions through a different client than the turn was started with.
Solutions
- Let the current turn finish/fail, then start a new turn so the route is re-captured; do not change models/endpoints mid-turn
- Revert the config/model change made during the turn and retry
- If an alias changed unexpectedly, pin the provider/model explicitly instead of relying on auto resolution
Defensive patterns
Strategy: validation
Validate before calling
let validated = provider_client.validate().map_err(anyhow::Error::msg)?;
if validated.identity.key != route.provider_identity
|| validated.model != route.model
|| validated.candidate.endpoint().base_url != route.base_url {
eprintln!("route changed; start a new turn instead of reusing the client");
return;
} Type guard
fn route_matches(v: &Validated, r: &TurnRoute) -> bool {
v.identity.key == r.provider_identity
&& v.model == r.model
&& v.candidate.endpoint().base_url == r.base_url
} Try / catch
match exact_translation_client(...).await {
Err(e) if e.to_string().contains("route changed") => {
abort_turn_and_prompt_restart();
}
r => r,
} Prevention
- Don't switch models, profiles, or endpoints while a turn is in flight
- Pin provider/model explicitly to avoid alias drift mid-turn
- Treat route mismatch as turn-fatal: fail fast and start a fresh turn
When it happens
Trigger: Calling `exact_translation_client` (from `run_event_loop`) when re-resolving the route yields a different `identity.key`, `model`, or `candidate.endpoint().base_url` than the `route` captured when the turn was dispatched — e.g. config/model profile changed mid-turn.
Common situations: User edits config or switches model profile while a turn is streaming; provider alias now resolves to another endpoint; persisted route cache updated between dispatch and completion.
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
- saved cache route identity
- Failed to resolve runtime thread route
- Fleet task ` ` has no provider authority for model ` `…
- Fleet task ` ` names custom provider=` `, but a provider…
- speech synthesis requires provider 'xiaomi-mimo
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/feb5c731d21f41f7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/ui/event_loop.rs:313
let identity = config
.resolve_persisted_provider_identity(
Some(route.provider.as_str()),
Some(&route.provider_identity),
)
.map_err(anyhow::Error::msg)?;
let validated = crate::route_runtime::resolve_runtime_route_for_identity(
config,
&identity,
Some(&route.model),
)
.map_err(anyhow::Error::msg)?
.validate()
.map_err(anyhow::Error::msg)?;
if validated.identity.key != route.provider_identity
|| validated.model != route.model
|| validated.candidate.endpoint().base_url != route.base_url
{
anyhow::bail!(
"translation route changed after turn dispatch; refusing to reuse a different provider client"
);
}
if let Some(receipt) = route.receipt.as_ref()
&& &validated
.client
.turn_route_receipt(&route.provider_identity)
!= receipt
{
anyhow::bail!(
"translation credential or endpoint changed after turn dispatch; refusing stale completion ownership"
);
}
Ok(Arc::new(validated.client))
}
/// Bind the Runtime thread store to a session before the process-owner lock
/// is taken, so a second Codewhale on the same machine does not collide onView on GitHub (pinned to 73e0f67d83)