Hmbown/CodeWhale · error · anyhow
Source changed; refresh the import preview
Error message
Source changed; refresh the import preview
What it means
The reviewed candidate was found by id, but its freshly re-computed `content_hash` no longer matches the hash embedded in the review token. The source file changed between preview and apply, so the earlier approval no longer describes what would be imported.
Solutions
- Re-run `/mcp import` to refresh the preview and get a token bound to the current hash.
- Diff the source config to confirm the change was expected before re-approving.
- Apply promptly after previewing to shrink the window for concurrent edits.
Example fix
// before codewhale mcp import apply <token-with-old-hash> // after codewhale mcp import preview # token now matches current hash codewhale mcp import apply <new-token>
Defensive patterns
Strategy: retry
Validate before calling
let (candidates, _) = context.discover();
let fresh = candidates.iter().find(|c| candidate_id(c) == id);
let matches = fresh.map(|c| c.content_hash == token_hash).unwrap_or(false);
if !matches { /* refresh preview */ } Try / catch
match apply_reviewed_import(...) {
Err(e) if e.to_string().contains("Source changed") => {
// refresh preview, re-review the new content, retry with the new token
}
other => other?,
} Prevention
- Avoid editing the source config between preview and apply
- Apply tokens promptly
- Re-run /mcp import after any tool that rewrites external configs
When it happens
Trigger: Approving an import after the external source's config file was edited (server URL, command, args, or env changed) since the preview was generated.
Common situations: The source application rewrote its own config (updated credentials, added fields); another sync tool (dotfile manager) touched the file between preview and apply; the user edited it manually.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- overlay is stale; run
- Reviewed source is unavailable; refresh the import preview
- A managed, project or plugin connector already uses this…
- Antigravity import requires an agy_cli grant, not
- audited owned root does not match mutation target
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/ce772e11ba32587e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/external_import.rs:679
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;
config.servers.insert(candidate.name.clone(), server);
}View on GitHub (pinned to 73e0f67d83)