{"record":{"id":"2fbc2d754e1796f1","repo":"jdx/mise","slug":"invalid-opt-record-for-brew-name-at-expected-a-symbolic-link","errorCode":null,"errorMessage":"invalid opt record for brew:{name} at {}: expected a symbolic link; inspect it and restore the formula's correct opt link","messagePattern":"invalid opt record for brew:(.+?) at (.+?): expected a symbolic link; inspect it and restore the formula's correct opt link","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/pour.rs","lineNumber":72,"sourceCode":"    let prefix = prefix::prefix();\n    let prefix = if prefix.is_absolute() {\n        prefix\n    } else {\n        std::env::current_dir()\n            .wrap_err_with(|| {\n                format!(\n                    \"cannot resolve relative Homebrew prefix {}\",\n                    prefix.display()\n                )\n            })?\n            .join(prefix)\n    };\n    let opt = prefix.join(\"opt\").join(name);\n    let rack = prefix.join(\"Cellar\").join(name);\n    let metadata =\n        std::fs::symlink_metadata(&opt).map_err(|err| package_root_io_error(name, &opt, err))?;\n    if !metadata.is_symlink() {\n        bail!(\n            \"invalid opt record for brew:{name} at {}: expected a symbolic link; inspect it and restore the formula's correct opt link\",\n            opt.display()\n        );\n    }\n    let target =\n        std::fs::canonicalize(&opt).map_err(|err| package_root_io_error(name, &opt, err))?;\n    let canonical_rack =\n        std::fs::canonicalize(&rack).map_err(|err| package_root_io_error(name, &rack, err))?;\n    let metadata =\n        std::fs::metadata(&target).map_err(|err| package_root_io_error(name, &target, err))?;\n    if !metadata.is_dir() || target.parent() != Some(canonical_rack.as_path()) {\n        bail!(\n            \"invalid opt record for brew:{name} at {}: target {} must be a directory directly inside {}; inspect it and restore the formula's correct opt link\",\n            opt.display(),\n            target.display(),\n            canonical_rack.display()\n        );\n    }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/pour.rs#L54-L90","documentation":"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.","triggerScenarios":"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/.","commonSituations":"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/.","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"],"exampleFix":"# before: opt/node is a plain directory\nrm -rf \"$(brew --prefix)/opt/node\"\nln -s \"$(brew --prefix)/Cellar/node/22.14.0\" \"$(brew --prefix)/opt/node\"","handlingStrategy":"validation","validationCode":"st=$(stat -c '%F' \"$(brew --prefix)/opt/$name\" 2>/dev/null) || true\n[ \"$st\" = \"symbolic link\" ] || echo \"opt/$name is $st, not a symlink — repair before applying\"","typeGuard":null,"tryCatchPattern":"match err.message() {\n  m if m.contains(\"expected a symbolic link\") => repair_opt_symlink(name),\n  _ => return Err(err),\n}","preventionTips":["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"],"tags":["brew","symlink","corrupt-state"],"backgroundTag":"broken-symlink","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}