Hmbown/CodeWhale · error
{reason}
Error message
{reason} What it means
When restoring a persisted thread, resolve_persisted_provider_identity reconstructs the ProviderIdentity from the thread's saved provider kind/id; its failure reason is rethrown verbatim via anyhow!. It is thrown when the persisted provider kind/id strings cannot be mapped to a live provider identity in the current configuration, so the thread's route cannot be preflighted.
Solutions
- Compare the persisted kind/id (shown in the reason) against providers configured now and add the missing provider back.
- Update the thread's stored provider fields to an existing provider identity.
- Fall back to the session's active provider identity for the restored thread if the original is gone.
- Clean up orphaned threads referencing removed providers.
Example fix
// before: thread persisted with removed provider id "model_provider": "openrouter-x", "model_provider_id": "org/legacy" // after: re-point the thread to a live provider "model_provider": "anthropic", "model_provider_id": "anthropic"
Defensive patterns
Strategy: try-catch
Validate before calling
// before resolving, check the persisted provider kind/id exist
if let (Some(kind), Some(id)) = (thread.model_provider.as_deref(), thread.model_provider_id.as_deref()) {
anyhow::ensure!(cfg.provider_kind_exists(kind) && cfg.provider_id_exists(id),
"thread references unknown provider {kind}/{id}");
}
Try / catch
let route = match restored_kind.as_deref() {
Some(kind) => match thread_config.resolve_persisted_provider_identity(Some(kind), restored_id.as_deref()) {
Ok(identity) => resolve_runtime_thread_route_for_identity(cfg, &identity, Some(&model)),
Err(reason) => {
log::warn!("persisted provider unusable ({reason}); falling back to active provider");
resolve_runtime_thread_route_for_identity(cfg, &cfg.active_provider_identity(cfg.api_provider())?, None)
}
},
None => resolve_runtime_thread_route_for_identity(cfg, &provider_identity, None),
};
Prevention
- When removing a provider from config, update or delete threads persisted against it.
- Store a fallback provider in session state for restoration.
- Version-tag persisted thread fields and migrate them on upgrade.
- Log and surface restoration failures at session load, not at turn start.
When it happens
Trigger: Loading a thread whose persisted model_provider / model_provider_id no longer match any configured provider, then attempting route resolution via the Some((restored_kind, restored_id, model)) branch.
Common situations: Config edited between sessions to remove/rename a custom provider; version upgrade renamed provider kinds; copied session files referencing another machine's providers; typo in the persisted id in the session store.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Failed to resolve runtime thread route
- This Codewhale account is configured for agent provider
- 127
- A pinned task provider requires an explicit model
- A provider and page loader are required.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8d8129b7e59f274a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:4870
let model = turn.effective_model?.trim().to_string();
let provider_kind = turn
.effective_provider
.filter(|provider| !provider.trim().is_empty());
// Preserve an explicitly empty additive id so malformed
// imported receipts fail closed instead of becoming an
// id-less legacy custom route.
let provider_id = turn.effective_provider_id;
((provider_kind.is_some() || provider_id.is_some()) && !model.is_empty())
.then_some((provider_kind, provider_id, model))
});
match restored {
Some((restored_kind, restored_id, model)) => {
let identity = thread_config
.resolve_persisted_provider_identity(
restored_kind.as_deref(),
restored_id.as_deref(),
)
.map_err(|reason| anyhow!(reason))?;
resolve_runtime_thread_route_for_identity(config, &identity, Some(&model))
}
None => resolve_runtime_thread_route_for_identity(config, &provider_identity, None),
}
}
fn provider_identity_for_thread(
&self,
config: &Config,
thread: &ThreadRecord,
) -> Result<ProviderIdentity> {
let has_persisted_route = thread
.model_provider
.as_deref()
.is_some_and(|provider| !provider.trim().is_empty())
|| thread.model_provider_id.is_some();
let identity = if has_persisted_route {
config.resolve_persisted_provider_identity(View on GitHub (pinned to 73e0f67d83)