jdx/mise · error
invalid opt record for brew
Error message
invalid opt record for brew:{name} at {}: expected a symbolic link; inspect it and restore the formula's correct opt link What it means
Homebrew maintains `opt/<formula>` as a symlink to the active keg under Cellar. strict_package_root requires this record to be a symbolic link; if `opt/<name>` exists as a regular file, directory, or other non-symlink entry, mise treats the install record as corrupt and refuses to use (or silently repair) it, telling you to restore the correct opt link.
Solutions
- Inspect the path: `ls -la $(brew --prefix)/opt/<name>` to see what replaced the symlink
- Remove the bogus entry and recreate the link: `rm -rf $(brew --prefix)/opt/<name> && ln -s ../Cellar/<name>/<version> $(brew --prefix)/opt/<name>`
- Re-run `mise bootstrap packages apply brew:<name>` to verify the record validates
Example fix
# before: opt/node is a plain directory rm -rf "$(brew --prefix)/opt/node" ln -s "$(brew --prefix)/Cellar/node/22.14.0" "$(brew --prefix)/opt/node"
Defensive patterns
Strategy: validation
Validate before calling
st=$(stat -c '%F' "$(brew --prefix)/opt/$name" 2>/dev/null) || true [ "$st" = "symbolic link" ] || echo "opt/$name is $st, not a symlink — repair before applying"
Try / catch
match err.message() {
m if m.contains("expected a symbolic link") => repair_opt_symlink(name),
_ => return Err(err),
} Prevention
- Back up the brew prefix with symlink-preserving tools (tar, rsync -a)
- Don't create files inside $(brew --prefix)/opt manually
- Run `mise bootstrap packages status` after restores or prefix migrations
When it happens
Trigger: Calling strict_package_root for a formula whose `$(brew --prefix)/opt/<name>` exists but is not a symlink — e.g. after a failed install left a directory there, a backup/restore turned the symlink into a real path, or something copied a file into opt/.
Common situations: Restoring the brew prefix from a tarball without symlink preservation (or on a filesystem that flattens symlinks); a crashed pour interrupted mid-link; manually experimenting in opt/.
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/2fbc2d754e1796f1.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/pour.rs:72
let prefix = prefix::prefix();
let prefix = if prefix.is_absolute() {
prefix
} else {
std::env::current_dir()
.wrap_err_with(|| {
format!(
"cannot resolve relative Homebrew prefix {}",
prefix.display()
)
})?
.join(prefix)
};
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()
);
}View on GitHub (pinned to 533346cc37)