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

  1. Inspect config.toml for the route/model entries just written and fix or remove the invalid value.
  2. Re-run the switch from a known-good config; check for a partially mutated file since the atomic writer should not have committed.
  3. 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

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


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)