{"record":{"id":"019fceebf5881236","repo":"jdx/mise","slug":"brew-cask-refusing-elevated-operation-because-tar","errorCode":null,"errorMessage":"brew-cask: refusing elevated operation because target appeared: {}","messagePattern":"brew-cask: refusing elevated operation because target appeared: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":2038,"sourceCode":"fn strict_elevated_directory_is_trusted(\n    directory: &Path,\n    stable_prefix: &Path,\n    uid: u32,\n    mode: u32,\n) -> bool {\n    uid == 0\n        && mode & 0o002 == 0\n        // Intel Homebrew conventionally uses root:admin 0775 for /usr/local.\n        // Permit that exact prefix, but require every descendant and every\n        // other ancestor used by the elevated operation to be non-writable.\n        && (mode & 0o020 == 0 || directory == stable_prefix)\n}\n\n#[cfg(unix)]\nfn ensure_target_absent(target: &Path) -> Result<()> {\n    match target.symlink_metadata() {\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),\n        Ok(_) => bail!(\n            \"brew-cask: refusing elevated operation because target appeared: {}\",\n            target.display()\n        ),\n        Err(err) => Err(err.into()),\n    }\n}\n\n#[cfg(unix)]\nfn copy_cask_artifact_at<Fd: std::os::fd::AsFd>(\n    from: &Path,\n    parent: Fd,\n    name: &std::ffi::OsStr,\n) -> Result<()> {\n    use std::os::unix::fs::PermissionsExt;\n\n    let metadata = from.symlink_metadata()?;\n    let mode = nix::sys::stat::Mode::from_bits_truncate(\n        metadata.permissions().mode() as nix::libc::mode_t","sourceCodeStart":2020,"sourceCodeEnd":2056,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L2020-L2056","documentation":"ensure_target_absent (src/system/packages/brew/cask.rs:2038) is the final pre-sudo existence check: immediately before a privileged copy the destination must not exist (symlink_metadata must report NotFound). If the target appeared after earlier checks — or was there all along — mise refuses rather than overwrite through sudo; a TOCTOU guard against both races and accidental clobbering of existing files with elevated privileges.","triggerScenarios":"Another process creating the destination file while mise's install runs between its earlier checks and the privileged copy: a concurrent mise/brew install, a management agent, or an app self-healing helper; also leftover targets from a previous failed run that the flow did not expect.","commonSituations":"Parallel installs of the same cask on shared machines; re-running a failed install whose target file survived; launchd/cron agents recreating binaries mid-provisioning.","solutions":["Inspect the target path in the error: if it is debris from a failed run, remove it (sudo rm) and retry","If another tool owns the file, stop and let that tool manage it — remove the cask request from mise instead","Serialize cask installs on shared or managed machines so no two writers race the same target"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"match target.symlink_metadata() {\n    Ok(_) => return Err(anyhow!(\"target exists - resolve before elevated copy\")),\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}\n    Err(e) => return Err(e.into()),\n}","typeGuard":null,"tryCatchPattern":"On catch, inspect the target path: if it is debris from a failed run, remove it and retry the install exactly once; if another tool created it, stop and let that tool own the file.","preventionTips":["Serialize mise cask installs on shared machines","Clean partial installs before re-running (remove the named target)","Watch for management agents that recreate binaries during provisioning windows"],"tags":["brew-cask","toctou","concurrency","elevated-copy"],"backgroundTag":"toctou-race-detected","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}