Hmbown/CodeWhale · error · anyhow
Reviewed source is unavailable; refresh the import preview
Error message
Reviewed source is unavailable; refresh the import preview
What it means
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.
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.
Example fix
// before codewhale mcp import apply <stale-token> // after codewhale mcp import preview # regenerate tokens codewhale mcp import apply <fresh-token>
Defensive patterns
Strategy: retry
Validate before calling
let (candidates, _) = context.discover();
if !candidates.iter().any(|c| candidate_id(c) == id) {
// token is stale: regenerate preview before applying
} Try / catch
match apply_reviewed_import(...) {
Err(e) if e.to_string().contains("Reviewed source is unavailable") => {
// regenerate preview tokens via /mcp import and retry once
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- A managed, project or plugin connector already uses this…
- computer_observation_required
- Invalid MCP entry; contents omitted
- MCP configuration changed; reload it before connecting this…
- MCP configuration changed; reload it before retrying this…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1347d9b632ec107f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/external_import.rs:677
/// completed import into a false failed-write receipt.
pub fn apply_reviewed_import(
context: &ImportContext<'_>,
id: &str,
hash: &str,
revision: &str,
decision: ImportDecision,
) -> anyhow::Result<ImportReceipt> {
anyhow::ensure!(
matches!(decision, ImportDecision::Approve | ImportDecision::Decline),
"Choose approve or decline"
);
let (candidate, revision) = super::mutate_config(context.mcp_path, Some(revision), |config| {
let (candidates, _) = context.discover();
let candidate = candidates
.into_iter()
.find(|candidate| candidate_id(candidate) == id)
.ok_or_else(|| {
anyhow::anyhow!("Reviewed source is unavailable; refresh the import preview")
})?;
anyhow::ensure!(
candidate.content_hash == hash,
"Source changed; refresh the import preview"
);
if decision == ImportDecision::Approve {
anyhow::ensure!(
!source_blocked(context, &candidate),
"Source is disabled or its workspace is untrusted"
);
anyhow::ensure!(
!context.merged()?.servers.contains_key(&candidate.name)
&& !config.servers.contains_key(&candidate.name),
"A managed, project or plugin connector already uses this name"
);
let mut server = candidate.server.clone();
server.enabled = false;
server.disabled = true;View on GitHub (pinned to 73e0f67d83)