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

  1. Compare the persisted kind/id (shown in the reason) against providers configured now and add the missing provider back.
  2. Update the thread's stored provider fields to an existing provider identity.
  3. Fall back to the session's active provider identity for the restored thread if the original is gone.
  4. 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 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


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)