jdx/mise · error
invalid opt record for brew
Error message
invalid opt record for brew:{name} at {}: target {} must be a directory directly inside {}; inspect it and restore the formula's correct opt link What it means
After confirming `opt/<formula>` is a symlink, strict_package_root canonicalizes it and requires the target to be a directory whose parent is exactly the formula's Cellar rack (`Cellar/<formula>`). This proves the opt link points at a real keg inside this prefix's Cellar. A dangling target, a target outside Cellar, or a versioned directory elsewhere (e.g. into another prefix, or a file) makes the record invalid.
Solutions
- Inspect the link target: `readlink -f $(brew --prefix)/opt/<name>` and compare with `ls $(brew --prefix)/Cellar/<name>/`
- Remove and recreate the link to the keg actually in Cellar: `ln -sfn ../Cellar/<name>/<version> $(brew --prefix)/opt/<name>`
- If the keg is genuinely missing, re-apply the package: `mise bootstrap packages apply brew:<name>`
Example fix
# before: opt/python -> /opt/old-brew/Cellar/python/3.12 (foreign prefix) ln -sfn "../Cellar/python/3.13.2" "$(brew --prefix)/opt/python"
Defensive patterns
Strategy: validation
Validate before calling
t=$(readlink -f "$(brew --prefix)/opt/$name") case "$t" in "$(brew --prefix)/Cellar/$name"/*) ;; *) echo "opt/$name points outside Cellar/$name: $t" ;; esac
Prevention
- Never hand-point opt/<name> at directories outside Cellar/<name>
- Recreate opt links after moving or symlinking the brew prefix (targets must canonicalize inside the rack)
- Verify with `readlink -f` after any manual prefix surgery
When it happens
Trigger: The opt symlink resolves but its canonicalized target is not a directory directly inside Cellar/<name> — e.g. link points into another Homebrew prefix, a relocated/stale path, a version directory that was moved, or a file instead of a keg directory.
Common situations: Prefixes migrated or symlinked between locations leaving stale absolute targets; manually relinking opt to an extracted tarball outside Cellar; upgrading tools that moved the keg.
Related errors
- invalid opt record for brew
- binary target is not an owned Caskroom symlink
- brew-cask: binary artifact
- brew-cask: refusing generic artifact source outside the…
- brew-cask: staged symlink path escaped extraction root
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/802f47f6c19b57e9.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/pour.rs:84
};
let opt = prefix.join("opt").join(name);
let rack = prefix.join("Cellar").join(name);
let metadata =
std::fs::symlink_metadata(&opt).map_err(|err| package_root_io_error(name, &opt, err))?;
if !metadata.is_symlink() {
bail!(
"invalid opt record for brew:{name} at {}: expected a symbolic link; inspect it and restore the formula's correct opt link",
opt.display()
);
}
let target =
std::fs::canonicalize(&opt).map_err(|err| package_root_io_error(name, &opt, err))?;
let canonical_rack =
std::fs::canonicalize(&rack).map_err(|err| package_root_io_error(name, &rack, err))?;
let metadata =
std::fs::metadata(&target).map_err(|err| package_root_io_error(name, &target, err))?;
if !metadata.is_dir() || target.parent() != Some(canonical_rack.as_path()) {
bail!(
"invalid opt record for brew:{name} at {}: target {} must be a directory directly inside {}; inspect it and restore the formula's correct opt link",
opt.display(),
target.display(),
canonical_rack.display()
);
}
Ok(opt)
}
/// Retain the filesystem error while adding lookup context and actionable recovery guidance.
fn package_root_io_error(name: &str, path: &Path, err: std::io::Error) -> eyre::Report {
let context = if err.kind() == std::io::ErrorKind::NotFound {
format!(
"brew:{name} has no usable installed opt link: {}; install or reconcile it with `mise bootstrap packages apply brew:{name}`",
path.display()
)
} else {
format!(View on GitHub (pinned to 533346cc37)