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

  1. Send a new turn through the normal request path so a route receipt for the current endpoint is recorded, then retry the warmup.
  2. If the endpoint change is unintended, revert the model/provider/base URL configuration back to the previous endpoint and retry.
  3. 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

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


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)