Hmbown/CodeWhale · warning
the cache route endpoint changed since the last turn; send…
Error message
the cache route endpoint changed since the last turn; send a new turn before warming
What it means
resolve_cache_replay_route refuses to warm the KV cache when the selected route's endpoint identity differs from the endpoint used in the previous turn. Warming a cache against a different base URL would be wasted or incorrect work, since the cached prefix belongs to the old endpoint. The error forces a fresh turn to establish the new route before warmup.
Solutions
- Send a new turn through the normal request path so a route receipt for the current endpoint is recorded, then retry the warmup.
- If the endpoint change is unintended, revert the model/provider/base URL configuration back to the previous endpoint and retry.
- Clear or update the stored route receipt (target.base_url) so it matches the current route's endpoint.
Example fix
// before: warm immediately after changing providers
run_cache_warmup(...).await?;
// after: only warm when the endpoint is unchanged, otherwise send a turn first
if endpoint_identity(&target.base_url) == endpoint_identity(&route.candidate.endpoint().base_url) {
run_cache_warmup(...).await?;
} else {
// send a normal turn to re-record the route, then warm
} Defensive patterns
Strategy: validation
Validate before calling
let prev = crate::route_receipt::endpoint_identity(target.base_url?);
let cur = crate::route_receipt::endpoint_identity(route.candidate.endpoint().base_url);
if prev != cur { /* send a turn first, skip warmup */ } Try / catch
match resolve_cache_replay_route(...) {
Ok(route) => warm(route),
Err(e) if e.to_string().contains("endpoint changed") => send_new_turn_then_retry(),
Err(e) => return Err(e),
} Prevention
- Only run cache warmup immediately after a successful turn on the same route.
- Compare endpoint identity before every warmup call.
- Treat provider/base URL switches as invalidating all pending warmups.
When it happens
Trigger: Calling run_cache_warmup when target.base_url is set and crate::route_receipt::endpoint_identity(previous_base_url) != endpoint_identity(route.candidate.endpoint().base_url) — i.e. the auto route provider/base URL changed since the last recorded turn.
Common situations: The user switched model providers or base URL between turns (config change, route failover, registry update) and then triggered a cache warmup before sending a new turn.
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
- Auto has no concrete route yet; send a turn before warming…
- saved cache route identity
- account_agent_model_unconfigured
- bounded fragment module missing required marker
- bounded provider catalog cache exceeds its write limit
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1faa958948f2ac2a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/ui/provider_routes.rs:426
.map_err(anyhow::Error::msg)?;
if identity.provider != target.provider || identity.key != target.provider_identity {
anyhow::bail!(
"saved cache route identity `{}` now resolves as {}/{} instead of {}/{}; send a new turn before warming",
target.provider_identity,
identity.provider.as_str(),
identity.key,
target.provider.as_str(),
target.provider_identity
);
}
let route = resolve_runtime_route_for_identity(config, &identity, Some(&target.model))
.map_err(anyhow::Error::msg)?;
if let Some(previous_base_url) = target.base_url.as_deref() {
let previous_endpoint = crate::route_receipt::endpoint_identity(previous_base_url);
let current_endpoint =
crate::route_receipt::endpoint_identity(&route.candidate.endpoint().base_url);
if previous_endpoint != current_endpoint {
anyhow::bail!(
"the cache route endpoint changed since the last turn; send a new turn before warming"
);
}
}
Ok(route)
}
pub(crate) fn error_health_route(
app: &App,
fallback_provider: ApiProvider,
) -> (ApiProvider, String) {
app.active_turn
.as_ref()
.and_then(|turn| turn.route.as_ref())
.map(|route| (route.provider, route.model.clone()))
.or_else(|| {
app.pending_turn_route
.as_ref()View on GitHub (pinned to 73e0f67d83)