{"record":{"id":"5a928da20b6f0530","repo":"jdx/mise","slug":"brew-cask-homebrew-took-ownership-of-this-cask","errorCode":null,"errorMessage":"brew-cask:{}: Homebrew took ownership of this cask while installation was pending","messagePattern":"brew-cask:(.+?): Homebrew took ownership of this cask while installation was pending","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":533,"sourceCode":"                    completion.source\n                );\n            }\n            for generated in &artifacts.generated_completions {\n                miseprintln!(\"generate completions from {}\", generated.executable);\n            }\n            return Ok(cask.version);\n        }\n        prefix::bootstrap(false)?;\n        let stage = fetch_and_stage(&cask, pr).await?;\n        let adopt = manager_options.brew_cask_adopt(&cask.token) && installed_version.is_none();\n        if adopt && !cask.auto_updates {\n            validate_adoptable_apps(&stage, &artifacts.apps)?;\n        }\n        let _caskroom_lock = lock_caskroom()?;\n        recover_flight_backups()?;\n        if homebrew_metadata_present(&cask.token) {\n            file::remove_all(&stage)?;\n            bail!(\n                \"brew-cask:{}: Homebrew took ownership of this cask while installation was pending\",\n                cask.token\n            );\n        }\n        if let Some(version) = installed_cask_version(&cask, &artifacts)?\n            && (cask.auto_updates || version == cask.version)\n        {\n            file::remove_all(stage)?;\n            return Ok(version);\n        }\n        let previous_binaries = previous_binary_targets(&cask)?;\n        let previous_fonts = previous_font_targets(&cask)?;\n        let previous_completions = previous_completion_targets(&cask)?;\n        let previous_flight_symlinks = previous_flight_symlink_targets(&cask)?;\n        let previous_flight_directories = previous_flight_directory_targets(&cask)?;\n        let previous_generic = previous_generic_targets(&cask)?;\n        let caskroom_token = caskroom_token_dir(&cask.token);\n        let caskroom = caskroom_version_dir(&cask.token, &cask.version);","sourceCodeStart":515,"sourceCodeEnd":551,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L515-L551","documentation":"A TOCTOU re-check in install_one_with_ancestors (src/system/packages/brew/cask.rs:533): after fetch_and_stage (network download + extraction, which happens before the caskroom lock is taken), mise re-runs homebrew_metadata_present under the caskroom lock. If Homebrew claimed the cask in the meantime, the staged tree is deleted and the install aborts so brew and mise never both write the caskroom.","triggerScenarios":"Concurrently running brew install --cask X (or brew bundle / brew upgrade) while mise install of the same token is in its download/extract phase; the earlier ownership check passed, the later one catches the takeover.","commonSituations":"Dotfile bootstraps that parallelize brew and mise provisioning; CI steps racing a Homebrew bundle against mise system-package setup; scheduled brew auto-update jobs firing mid-install.","solutions":["Decide who owns the cask: if Homebrew should not, run brew uninstall --cask <token>, then re-run the mise install serially","Serialize brew and mise installs of the same cask — never run them concurrently","Once ownership is consistent, a plain retry succeeds; the staged tree was already cleaned up by mise itself"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Catch the error, run `brew list --cask <token>` to confirm the takeover, `brew uninstall --cask <token>` if mise should own it, then retry the install serially — at most once.","preventionTips":["Never run brew and mise installs of the same cask concurrently; serialize dotfile provisioning steps","Disable scheduled brew auto-update jobs (brew autoupdate, cron brew bundle) before bulk mise provisioning","Treat a failed install as resumable — mise cleans its staged tree, so a plain retry is safe"],"tags":["brew-cask","concurrency","toctou","homebrew"],"backgroundTag":"package-ownership-conflict","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}