Hmbown/CodeWhale · error · anyhow::Error
Could not parse switched route; contents omitted
Error message
Could not parse switched route; contents omitted
What it means
reconcile_root_model_aliases re-parses the mutated document as Config to decide whether the root default_text_model alias is servable by the incoming route; if that re-parse fails the writer aborts with this error rather than rewriting a config it cannot validate. Contents are omitted from the message intentionally, and the writer never touches a document broken for unrelated reasons.
Solutions
- Inspect config.toml for the route/model entries just written and fix or remove the invalid value.
- Re-run the switch from a known-good config; check for a partially mutated file since the atomic writer should not have committed.
- Update Codewhale if a released writer produced a shape the current Config type rejects (writer/loader version mismatch).
Example fix
# before (route section with bad value) [route] text_model = "" # after [route] text_model = "claude-sonnet-4"
Defensive patterns
Strategy: try-catch
Validate before calling
let switched: Result<Config, _> = toml::from_str(&doc.to_string());
if switched.is_err() { eprintln!("post-mutation document no longer loadable; aborting alias reconcile"); } Try / catch
match persist_provider_selection(&path, p, ident, model) {
Err(e) if e.to_string().contains("Could not parse switched route") => {
// inspect config.toml for values written by an older/buggy writer
}
other => other?,
} Prevention
- Run the same Codewhale version for writing and loading configs.
- Validate the config after each writer change with a parse round-trip test.
- Do not edit route tables by hand while a switch is in flight.
When it happens
Trigger: After persist_provider_selection's document mutation, the serialized document no longer deserializes into Config (e.g. a route-specific model value written with an invalid shape) when reconcile_root_model_aliases parses the switched document.
Common situations: A route model string or route table written by a buggy or older writer makes the config fail validation; another process rewrote config.toml mid-switch.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Could not parse destination route; contents omitted
- Could not parse route configuration; contents omitted
- The destination config has a different provider identity
- active profile is missing or malformed
- agent profile provider cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/5d5e7acae8ac88ee.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config_persistence.rs:278
// route's leftovers.
if incoming.provider == ApiProvider::Custom && incoming.persisted_id().is_none() {
return Ok(());
}
// Only `default_text_model` is read here. The legacy root `model` key is
// never what blocks a load: `Config::default_model` already refuses to
// route a foreign legacy value to a provider that cannot serve it, and
// `Config::validate` does not consult it, so relocating it would move a
// value nothing is asking about.
const ROOT_KEY: &str = "default_text_model";
let Some(value) = doc
.get(ROOT_KEY)
.and_then(toml_edit::Item::as_str)
.map(str::to_owned)
else {
return Ok(());
};
let switched: crate::config::Config = toml::from_str(&doc.to_string())
.map_err(|_| anyhow::anyhow!("Could not parse switched route; contents omitted"))?;
// `Config::validate` is the single authority on what the incoming route can
// serve, so a writer cannot disagree with the loader. Act only when this
// alias is what the loader rejects: a document already broken for an
// unrelated reason is not this writer's to rewrite.
let mut without_alias = switched.clone();
without_alias.default_text_model = None;
if switched
.provider_config_for(incoming.provider)
.and_then(|entry| entry.model.as_deref())
.is_some()
|| switched.validate().is_ok()
|| without_alias.validate().is_err()
{
return Ok(());
}
// An empty or control-bearing alias names no saved model. Nothing to
// relocate, and clearing it loses nothing.
if value.trim().is_empty() || value.chars().any(char::is_control) {View on GitHub (pinned to 73e0f67d83)