{"record":{"id":"1347d9b632ec107f","repo":"Hmbown/CodeWhale","slug":"reviewed-source-is-unavailable-refresh-the-import-preview","errorCode":null,"errorMessage":"Reviewed source is unavailable; refresh the import preview","messagePattern":"Reviewed source is unavailable; refresh the import preview","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/external_import.rs","lineNumber":677,"sourceCode":"/// completed import into a false failed-write receipt.\npub fn apply_reviewed_import(\n    context: &ImportContext<'_>,\n    id: &str,\n    hash: &str,\n    revision: &str,\n    decision: ImportDecision,\n) -> anyhow::Result<ImportReceipt> {\n    anyhow::ensure!(\n        matches!(decision, ImportDecision::Approve | ImportDecision::Decline),\n        \"Choose approve or decline\"\n    );\n    let (candidate, revision) = super::mutate_config(context.mcp_path, Some(revision), |config| {\n        let (candidates, _) = context.discover();\n        let candidate = candidates\n            .into_iter()\n            .find(|candidate| candidate_id(candidate) == id)\n            .ok_or_else(|| {\n                anyhow::anyhow!(\"Reviewed source is unavailable; refresh the import preview\")\n            })?;\n        anyhow::ensure!(\n            candidate.content_hash == hash,\n            \"Source changed; refresh the import preview\"\n        );\n        if decision == ImportDecision::Approve {\n            anyhow::ensure!(\n                !source_blocked(context, &candidate),\n                \"Source is disabled or its workspace is untrusted\"\n            );\n            anyhow::ensure!(\n                !context.merged()?.servers.contains_key(&candidate.name)\n                    && !config.servers.contains_key(&candidate.name),\n                \"A managed, project or plugin connector already uses this name\"\n            );\n            let mut server = candidate.server.clone();\n            server.enabled = false;\n            server.disabled = true;","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/external_import.rs#L659-L695","documentation":"During the review-apply transaction, the candidate list is re-discovered inside the config-mutation closure and matched against the id encoded in the review token. If no candidate with that id exists anymore, the token refers to a source that vanished. Codewhale refuses to apply against stale review state instead of guessing.","triggerScenarios":"Running `/mcp import approve <token>` (or `mcp_import_apply`) where the external source file that the token was generated from was deleted, renamed, or is no longer discoverable (e.g. the other MCP client was uninstalled or its config moved).","commonSituations":"The user generated the preview, then uninstalled/deleted the source app (e.g. removed Claude Desktop config); discovery happens on a different machine/container without the source config present.","solutions":["Re-run `/mcp import` to generate a fresh preview and new approve/decline tokens, then use the new token.","Verify the source config file still exists and is discoverable (correct home dir, plugin/workspace paths).","If the source was removed intentionally, decline or skip it — do not retry the old token."],"exampleFix":"// before\ncodewhale mcp import apply <stale-token>\n// after\ncodewhale mcp import preview   # regenerate tokens\ncodewhale mcp import apply <fresh-token>","handlingStrategy":"retry","validationCode":"let (candidates, _) = context.discover();\nif !candidates.iter().any(|c| candidate_id(c) == id) {\n    // token is stale: regenerate preview before applying\n}","typeGuard":null,"tryCatchPattern":"match apply_reviewed_import(...) {\n    Err(e) if e.to_string().contains(\"Reviewed source is unavailable\") => {\n        // regenerate preview tokens via /mcp import and retry once\n    }\n    other => other?,\n}","preventionTips":["Apply immediately after generating the preview token","Re-preview whenever the source app is installed, uninstalled, or updated","Check source existence before scripting bulk approvals"],"tags":["stale-state","import","mcp"],"backgroundTag":"entity-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}