jdx/mise · error
artifact target has changed
Error message
artifact target has changed: {} What it means
validate_cask_prune_candidate re-fingerprints every artifact target recorded in the cask receipt and refuses the prune if any target no longer matches its recorded fingerprint. This ensures the on-disk state is exactly what the install receipt captured before the prune mutates anything. A mismatch means something modified the artifact since installation and blindly pruning could destroy data or break the package.
Solutions
- Diff the target at record.path against the staged version copy; if the change is unwanted, restore the original before pruning.
- If the change is intentional user data, exclude that target from the prune plan or migrate the data first.
- Reinstall the cask to regenerate a consistent receipt and targets, then retry the prune.
Defensive patterns
Strategy: validation
Validate before calling
for record in &receipt.targets {
if !cask_target_record_matches(record)? {
eprintln!("target drifted: {}", record.path.display());
}
}
// fix or exclude drifted targets before requesting a prune Try / catch
match validate_cask_prune_candidate(&receipt, &candidate) {
Err(e) if e.to_string().contains("artifact target has changed") => {
// inspect/restore drifted targets, then retry
}
other => other?,
} Prevention
- Avoid editing files inside installed cask targets directly.
- Exclude install trees from file-sync/backup tools that rewrite file contents or metadata.
- Reinstall the cask if a target was modified, so receipt fingerprints match disk again.
When it happens
Trigger: Calling validate_cask_prune_candidate (via cask_prune_plan_from_tokens or apply_cask_prune_plan_in) when cask_target_record_matches returns false for any receipt.targets entry — i.e. the file/symlink at record.path changed kind, content, or link destination since the fingerprint was recorded.
Common situations: A user edited or replaced a binary/font/app target after install; a file-sync tool (Dropbox, git) modified the target; a partially failed upgrade left a target half-updated; the recorded path was overwritten by another package or a manual copy.
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
- receipt target inventory contains an unclassified path
- receipt target inventory is incomplete or duplicated
- app target is outside an allowed Applications directory
- binary target is not an owned Caskroom symlink
- brew-cask: ' ' must stay below Applications
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/6604b4da5841d09c.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/state.rs:1140
for path in &receipt.completions {
let record = records
.get(path)
.ok_or_else(|| eyre!("missing completion target record"))?;
if record.fingerprint.kind != CaskTargetKind::Symlink
|| !completion_roots
.iter()
.any(|root| path_is_below(path, root))
|| !symlink_resolves_below(path, &candidate.version_dir)
{
bail!(
"completion target is not an owned Caskroom symlink: {}",
path.display()
);
}
}
for record in &receipt.targets {
if !cask_target_record_matches(record)? {
bail!("artifact target has changed: {}", record.path.display());
}
}
Ok(())
}
pub(super) fn path_is_below(path: &Path, root: &Path) -> bool {
path.strip_prefix(root).is_ok_and(|relative| {
relative.components().next().is_some()
&& !relative
.components()
.any(|component| component == Component::ParentDir)
})
}
pub(super) fn staged_target_matches(record: &CaskTargetRecord, staged: &Path) -> bool {
cask_target_fingerprint(staged).is_ok_and(|fingerprint| fingerprint == record.fingerprint)
}
View on GitHub (pinned to 533346cc37)