Hmbown/CodeWhale · warning
Auto has no concrete route yet; send a turn before warming…
Error message
Auto has no concrete route yet; send a turn before warming its cache
What it means
Thrown by resolve_cache_replay_route when cache warmup is requested for the Auto routing mode before Auto has resolved to any concrete provider route. Auto picks a route only after a real turn has been sent; with no turn history there is no concrete provider/endpoint to replay against, so warmup cannot proceed.
Solutions
- Send at least one turn normally first; Auto then records a concrete route and warmup succeeds.
- Pin the route to a specific provider/model instead of Auto if you need warmup before any turn.
- Skip warmup when app.cache_replay_target() is None rather than calling resolve_cache_replay_route.
- If a turn was already sent but the target is still None, check that route resolution persisted the last concrete route.
Example fix
// before
let route = resolve_cache_replay_route(app, config)?;
warm_cache(route).await?;
// after
if let Ok(route) = resolve_cache_replay_route(app, config) {
warm_cache(route).await?;
} // else: Auto not yet resolved; skip warmup until first turn completes Defensive patterns
Strategy: fallback
Validate before calling
// skip warmup when Auto has not resolved a concrete route
fn warmup_ready(app: &App) -> bool {
app.cache_replay_target().is_some()
} Try / catch
match resolve_cache_replay_route(app, config) {
Ok(route) => warm_cache(route).await?,
Err(e) if e.to_string().contains("no concrete route") => {
tracing::debug!("skipping warmup: Auto not yet resolved");
}
Err(e) => return Err(e),
} Prevention
- Only trigger cache warmup after at least one completed turn in Auto mode.
- Pin an explicit provider/model route when warmup must run before any turn.
- Gate warmup calls on cache_replay_target().is_some().
- Re-check the resolved route after clearing conversation history.
When it happens
Trigger: Calling run_cache_warmup -> resolve_cache_replay_route while app.cache_replay_target() returns None — i.e. triggering prompt-cache warmup with route set to Auto in a fresh session with zero completed turns, or after state that cleared the last resolved route.
Common situations: Warming the cache immediately after launching a new session pinned to Auto; running warmup after clearing conversation history; scripts that invoke warmup before the first user turn completes.
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
- the cache route endpoint changed since the last turn; send…
- 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/ae096bed3013f529.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/ui/provider_routes.rs:401
}
pub(crate) async fn fetch_available_models(config: &Config) -> Result<Vec<String>> {
use crate::client::CodewhaleClient;
let client = CodewhaleClient::new(config)?;
let models = tokio::time::timeout(Duration::from_secs(20), client.list_models()).await??;
let mut ids = models.into_iter().map(|model| model.id).collect::<Vec<_>>();
ids.sort();
ids.dedup();
Ok(ids)
}
pub(crate) fn resolve_cache_replay_route(
app: &App,
config: &Config,
) -> Result<crate::route_runtime::ResolvedRuntimeRoute> {
let target = app.cache_replay_target().ok_or_else(|| {
anyhow::anyhow!("Auto has no concrete route yet; send a turn before warming its cache")
})?;
let identity = config
.resolve_persisted_provider_identity(
Some(target.provider.as_str()),
target.provider_id.as_deref(),
)
.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))View on GitHub (pinned to 73e0f67d83)