{"record":{"id":"4fc158a6f69dbb40","repo":"aaif-goose/goose","slug":"provider-changed-before-inventory-refresh-complete","errorCode":null,"errorMessage":"provider changed before inventory refresh completed","messagePattern":"provider changed before inventory refresh completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/goose/src/acp/server/dispatch.rs","lineNumber":231,"sourceCode":"                                let agent_bg = agent.clone();\n                                let cx_bg = cx.clone();\n                                let session_id_bg = session_id.clone();\n                                tokio::spawn(async move {\n                                    let refresh_identity = refresh_job.identity;\n                                    let refresh_provider_id = refresh_job.provider_id;\n                                    let mut refresh_guard =\n                                        agent_bg.provider_inventory.refresh_guard(&refresh_identity);\n                                    let provider_result: Result<Arc<dyn Provider>> =\n                                        AssertUnwindSafe(async {\n                                            let session_agent =\n                                                agent_bg.get_session_agent(&session_id_bg.0).await?;\n                                            let provider = session_agent\n                                                .provider()\n                                                .await\n                                                .map_err(|e| anyhow::anyhow!(e.to_string()))?;\n                                            let provider_name = provider.get_name().to_string();\n                                            if provider_name != refresh_provider_id {\n                                                return Err(anyhow::anyhow!(\n                                                    \"provider changed before inventory refresh completed\"\n                                                ));\n                                            }\n                                            Ok(provider)\n                                        })\n                                        .catch_unwind()\n                                .await\n                                .map_err(|_| {\n                                    anyhow::anyhow!(\"provider inventory refresh task panicked\")\n                                })\n                                .and_then(|result| result);\n\n                                let fetch_result = match provider_result {\n                                    Ok(provider) => {\n                                        match ensure_refresh_identity_current(\n                                            &refresh_provider_id,\n                                            &refresh_identity,\n                                        )","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/acp/server/dispatch.rs#L213-L249","documentation":"A background provider-inventory refresh captured a refresh_identity tied to a provider id, then re-fetched the session's provider and compared names; the session had switched to a different provider before the refresh finished. The refresh aborts so models fetched from the old provider are never stored under the new provider's identity — a consistency guard against a provider-switch race, not a data-loss failure.","triggerScenarios":"The user changes the model/provider in the UI while a recommended-models refresh for the previous provider is still in flight; the check 'provider_name != refresh_provider_id' then trips and the task errors out.","commonSituations":"Rapid provider switching in the desktop app while background refreshes are pending; flaky network making the original refresh slow enough that the user switches providers meanwhile.","solutions":["Treat it as benign: switch providers again or wait — the next refresh for the new provider runs with a fresh identity","Avoid changing providers repeatedly in quick succession while a model list is loading","If it persists, check that provider get_name() is stable across calls (custom providers returning dynamic names would trip this guard)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// guard result is consumed by the refresh task itself:\nif let Err(e) = refresh_result {\n    if e.to_string().contains(\"provider changed before\") {\n        tracing::debug!(%e, \"stale refresh skipped; new refresh already scheduled\");\n    } else {\n        tracing::warn!(%e, \"inventory refresh failed\");\n    }\n}","preventionTips":["Avoid switching providers while a model-list refresh is in flight","Treat this guard as informational — the new provider's refresh carries fresh data"],"tags":["race-condition","provider","inventory","background-task"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}