{"record":{"id":"8d8129b7e59f274a","repo":"Hmbown/CodeWhale","slug":"reason","errorCode":null,"errorMessage":"{reason}","messagePattern":"\\{reason\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":4870,"sourceCode":"                let model = turn.effective_model?.trim().to_string();\n                let provider_kind = turn\n                    .effective_provider\n                    .filter(|provider| !provider.trim().is_empty());\n                // Preserve an explicitly empty additive id so malformed\n                // imported receipts fail closed instead of becoming an\n                // id-less legacy custom route.\n                let provider_id = turn.effective_provider_id;\n                ((provider_kind.is_some() || provider_id.is_some()) && !model.is_empty())\n                    .then_some((provider_kind, provider_id, model))\n            });\n        match restored {\n            Some((restored_kind, restored_id, model)) => {\n                let identity = thread_config\n                    .resolve_persisted_provider_identity(\n                        restored_kind.as_deref(),\n                        restored_id.as_deref(),\n                    )\n                    .map_err(|reason| anyhow!(reason))?;\n                resolve_runtime_thread_route_for_identity(config, &identity, Some(&model))\n            }\n            None => resolve_runtime_thread_route_for_identity(config, &provider_identity, None),\n        }\n    }\n\n    fn provider_identity_for_thread(\n        &self,\n        config: &Config,\n        thread: &ThreadRecord,\n    ) -> Result<ProviderIdentity> {\n        let has_persisted_route = thread\n            .model_provider\n            .as_deref()\n            .is_some_and(|provider| !provider.trim().is_empty())\n            || thread.model_provider_id.is_some();\n        let identity = if has_persisted_route {\n            config.resolve_persisted_provider_identity(","sourceCodeStart":4852,"sourceCodeEnd":4888,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L4852-L4888","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: thread persisted with removed provider id\n\"model_provider\": \"openrouter-x\", \"model_provider_id\": \"org/legacy\"\n// after: re-point the thread to a live provider\n\"model_provider\": \"anthropic\", \"model_provider_id\": \"anthropic\"","handlingStrategy":"try-catch","validationCode":"// before resolving, check the persisted provider kind/id exist\nif let (Some(kind), Some(id)) = (thread.model_provider.as_deref(), thread.model_provider_id.as_deref()) {\n    anyhow::ensure!(cfg.provider_kind_exists(kind) && cfg.provider_id_exists(id),\n        \"thread references unknown provider {kind}/{id}\");\n}\n","typeGuard":null,"tryCatchPattern":"let route = match restored_kind.as_deref() {\n    Some(kind) => match thread_config.resolve_persisted_provider_identity(Some(kind), restored_id.as_deref()) {\n        Ok(identity) => resolve_runtime_thread_route_for_identity(cfg, &identity, Some(&model)),\n        Err(reason) => {\n            log::warn!(\"persisted provider unusable ({reason}); falling back to active provider\");\n            resolve_runtime_thread_route_for_identity(cfg, &cfg.active_provider_identity(cfg.api_provider())?, None)\n        }\n    },\n    None => resolve_runtime_thread_route_for_identity(cfg, &provider_identity, None),\n};\n","preventionTips":["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."],"tags":["persistence","provider","session-restore","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}