jdx/mise · error
binary target is not an owned Caskroom symlink
Error message
binary target is not an owned Caskroom symlink: {} What it means
Cask binary targets must be symlinks living under an allowed binary target root (e.g. a Homebrew bin dir) that resolve back into the cask's staged version directory. If a recorded binary target is not a Symlink-kind record, sits outside allowed roots, or its symlink no longer resolves below candidate.version_dir, mise refuses to prune — deleting would either break a foreign link or remove a file mise does not own.
Solutions
- Reinstall the cask with mise so the binary symlink is recreated pointing into the correct version dir: mise uninstall brew-cask:foo && mise install brew-cask:foo
- Inspect the binary path (ls -l) and restore it to a symlink resolving into the cask's Caskroom version directory before pruning
- Ensure the binary lives under an allowed binary root for the active Homebrew prefix; align HOMEBREW_PREFIX between the installing and pruning environments
- Leave that cask out of the prune plan and manage its binaries via brew
Example fix
// before: symlink replaced by a copied binary $ rm /usr/local/bin/foo && cp ~/staging/foo /usr/local/bin/foo // after: restore owned symlink into the version dir ln -sf "$(mise --prefix brew-cask:foo)/Caskroom/foo/1.2.3/foo.bin" /usr/local/bin/foo
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: each binary target must be a symlink into the version dir
fn binaries_are_owned_symlinks(receipt: &CaskReceipt, version_dir: &Path) -> bool {
let roots = allowed_binary_target_roots();
receipt.binaries.iter().all(|p| {
p.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false)
&& roots.iter().any(|r| path_is_below(p, r))
&& symlink_resolves_below(p, version_dir)
})
} Type guard
fn is_owned_caskroom_symlink(path: &Path, version_dir: &Path) -> bool {
path.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false)
&& symlink_resolves_below(path, version_dir)
} Try / catch
match apply_cask_prune_plan_in(&plan) {
Ok(removed) => info!("pruned {removed} casks"),
Err(e) if e.to_string().contains("not an owned Caskroom symlink") => {
warn!("binary link replaced externally; reinstalling cask: {e:#}");
// recreate the symlink or fall back to brew management
}
Err(e) => return Err(e),
} Prevention
- Never replace cask-managed bin symlinks with copies or wrapper scripts
- Keep the Homebrew prefix consistent between install and prune environments
- Use mise uninstall/install to change versions instead of relinking binaries by hand
- Run ls -l on receipt.binaries paths before pruning to confirm they are symlinks into the Caskroom
When it happens
Trigger: Calling cask_prune_plan_from_tokens or apply_cask_prune_plan_in when a receipt.binaries path was replaced by a real file (not a symlink), the symlink points outside the cask's version_dir (e.g. into a different version or a manually managed location), or the binary was installed to a nonstandard prefix outside allowed_binary_target_roots().
Common situations: User replaced the symlink with a copied binary or their own wrapper script; brew or the user relinked the binary to another cask version; PATH tooling rewrote links in /usr/local/bin; custom HOMEBREW_PREFIX so binaries sit outside the roots this mise recognizes.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- app target is outside an allowed Applications directory
- completion target is not an owned Caskroom symlink
- font target is outside the platform font directory
- app Info.plist must be a regular file
- artifact target has changed
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/89409f3a9cd01ad4.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/state.rs:1092
})
{
bail!(
"app target is outside an allowed Applications directory: {}",
path.display()
);
}
}
for path in &receipt.binaries {
let record = records
.get(path)
.ok_or_else(|| eyre!("missing binary target record"))?;
if record.fingerprint.kind != CaskTargetKind::Symlink
|| !allowed_binary_target_roots()
.iter()
.any(|root| path_is_below(path, root))
|| !symlink_resolves_below(path, &candidate.version_dir)
{
bail!(
"binary target is not an owned Caskroom symlink: {}",
path.display()
);
}
}
for path in &receipt.fonts {
let record = records
.get(path)
.ok_or_else(|| eyre!("missing font target record"))?;
let fonts = font_dir();
if record.fingerprint.kind != CaskTargetKind::File
|| !path_is_below(path, &fonts)
|| !path.strip_prefix(&fonts).is_ok_and(|relative| {
staged_target_matches(record, &candidate.version_dir.join(relative))
})
{
bail!(
"font target is outside the platform font directory: {}",View on GitHub (pinned to 533346cc37)