{"record":{"id":"1a42fe7ac2a5e489","repo":"jdx/mise","slug":"brew-cask-staging-directory-is-not-owned-by-the-c-1a42fe","errorCode":null,"errorMessage":"brew-cask: staging directory is not owned by the current user","messagePattern":"brew-cask: staging directory is not owned by the current user","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2431,"sourceCode":"/// A racing creation of `name` surfaces as `EEXIST` and fails closed rather than\n/// being followed.\n#[cfg(unix)]\nfn ditto_into<Fd: std::os::fd::AsFd>(from: &Path, dir: Fd, name: &std::ffi::OsStr) -> Result<()> {\n    nix::sys::stat::mkdirat(&dir, name, nix::sys::stat::Mode::S_IRWXU).wrap_err_with(|| {\n        format!(\n            \"brew-cask: cannot create staging directory {}\",\n            Path::new(name).display()\n        )\n    })?;\n    let destination = open_dir_nofollow_at(&dir, name).wrap_err_with(|| {\n        format!(\n            \"brew-cask: cannot open staging directory {}\",\n            Path::new(name).display()\n        )\n    })?;\n    let stat = nix::sys::stat::fstat(&destination)?;\n    if stat.st_uid != nix::unistd::geteuid().as_raw() {\n        bail!(\"brew-cask: staging directory is not owned by the current user\");\n    }\n    // `ditto src dst` copies the *contents* of src into dst, so pointing it at\n    // the bound directory reproduces the bundle in place.\n    let status = run_in_trusted_dir(\n        \"ditto\",\n        &[from.as_os_str(), std::ffi::OsStr::new(\".\")],\n        &destination,\n    )?;\n    if !status.success() {\n        bail!(\n            \"ditto failed copying {} to {}\",\n            from.display(),\n            Path::new(name).display()\n        );\n    }\n    // Restore the bundle's own permissions, which the private staging mode hid.\n    if let Ok(metadata) = from.symlink_metadata() {\n        use std::os::unix::fs::PermissionsExt;","sourceCodeStart":2413,"sourceCodeEnd":2449,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L2413-L2449","documentation":"Before copying bundle contents into the destination staging directory with ditto, the library opens the directory and fstat()s it, verifying it is owned by the current effective uid. If the staging directory is owned by someone else (e.g. created by root or another user), proceeding could let an untrusted directory's contents be manipulated, so the operation is refused.","triggerScenarios":"Opening the pre-created staging directory (by name under the mise temp/cask dir) succeeds but fstat shows st_uid != geteuid() — the directory exists but belongs to root or another account.","commonSituations":"A previous install was run with sudo/root, leaving root-owned staging dirs; running mise under a different user than the one that seeded the cache; CI switching between users.","solutions":["Delete the root-owned staging directory (sudo rm -rf) and rerun the install as the normal user","Stop running mise installs with sudo; chown -R \"$USER\" the mise cache/temp directories if needed","In CI, ensure all steps run under the same user account"],"exampleFix":"// before\nsudo mise install <cask>   # leaves root-owned staging dirs\n// after\nsudo rm -rf \"$(mise cache dir)\"/.../staging-*\nchown -R \"$USER\" \"$(mise cache dir)\"\nmise install <cask>","handlingStrategy":"validation","validationCode":"use nix::sys::stat::fstat;\nlet stat = fstat(&dir)?;\nif stat.st_uid != nix::unistd::geteuid().as_raw() {\n    panic!(\"staging dir owned by uid {}; remove it and rerun as current user\", stat.st_uid);\n}","typeGuard":"fn staging_owned_by_me(dir: &impl nix::NixPath) -> bool {\n    nix::sys::stat::fstat(dir)\n        .map(|s| s.st_uid == nix::unistd::geteuid().as_raw())\n        .unwrap_or(false)\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"not owned by the current user\") => {\n        eprintln!(\"delete the root-owned staging dir and reinstall without sudo\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["Never run mise installs under sudo","chown -R \"$USER\" the mise cache/temp dirs after user changes","In CI, keep all install steps under one user account"],"tags":["permissions","ownership","security","brew-cask"],"backgroundTag":"permission-denied","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}