{"record":{"id":"7480042b359b653a","repo":"gitbutlerapp/gitbutler","slug":"could-not-remove-quarantine-attribute-e-macos","errorCode":null,"errorMessage":"Could not remove quarantine attribute: {e} - macOS may show security warnings","messagePattern":"Could not remove quarantine attribute: (.+?) - macOS may show security warnings","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":104,"sourceCode":"            .map(|c| format!(\" channel {}\", c.display_name()))\n            .unwrap_or_default(),\n        install_app.display()\n    ));\n\n    // Clean up any leftover temp files from previous failed installations\n    let _ = fs::remove_dir_all(&install_app_new);\n    let _ = fs::remove_dir_all(&install_app_backup);\n\n    // Create Applications directory if it doesn't exist\n    fs::create_dir_all(home_dir.join(\"Applications\"))?;\n\n    // Install to temporary location first\n    info(\"Installing to temporary location...\");\n    copy_dir_all(app_dir, &install_app_new)?;\n\n    // Remove macOS quarantine attribute\n    if let Err(e) = remove_quarantine_recursive(&install_app_new) {\n        warn(&format!(\n            \"Could not remove quarantine attribute: {e} - macOS may show security warnings\"\n        ));\n        info(\"If macOS blocks the app, go to System Settings > Privacy & Security and allow it\");\n    }\n\n    // Create bin directory\n    let bin_dir = home_dir.join(\".local/bin\");\n    fs::create_dir_all(&bin_dir)?;\n\n    // Check for existing 'but' and detect channel switching\n    let but_symlink = bin_dir.join(\"but\");\n    let but_new = bin_dir.join(\"but.new\");\n\n    if but_symlink.exists() && !but_symlink.is_symlink() {\n        // 'but' exists but is not a symlink\n        let now = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .unwrap_or_default()","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install_macos.rs#L86-L122","documentation":"The macOS installer strips the com.apple.quarantine extended attribute from the staged app bundle (remove_quarantine_recursive) so Gatekeeper does not treat the CLI-installed app as a blocked download. If stripping fails, the install continues — the consequence is deferred to the user: macOS may show 'app can't be opened because it is from an unidentified developer', resolved via System Settings > Privacy & Security > Allow Anyway.","triggerScenarios":"remove_quarantine_recursive(&install_app_new) returns Err at crates/but-installer/src/install_macos.rs:104 — the xattr tooling is missing (no Xcode Command Line Tools), some files in the bundle are not writable by the user, or SIP or MDM policy restricts attribute changes.","commonSituations":"A fresh Mac without Command Line Tools; bundles containing root-owned files from a previous manual install; managed or sandboxed Macs restricting xattr.","solutions":["If macOS blocks the app on first launch: System Settings > Privacy & Security > 'Allow Anyway'","Strip the attribute manually: xattr -dr com.apple.quarantine ~/Applications/<App>.app","Install Command Line Tools (xcode-select --install) and re-run the installer so removal succeeds"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Check whether the attribute is actually present before/after removal\nstd::process::Command::new(\"xattr\")\n    .args([\"-p\", \"com.apple.quarantine\"])\n    .arg(&install_app_new)\n    .output()\n    .map(|o| o.status.success())\n    .unwrap_or(false);","typeGuard":null,"tryCatchPattern":"if let Err(e) = remove_quarantine_recursive(&install_app_new) {\n    warn(&format!(\"Could not remove quarantine attribute: {e} - macOS may show security warnings\"));\n    info(\"If macOS blocks the app, go to System Settings > Privacy & Security and allow it\");\n}","preventionTips":["Install Command Line Tools before updating","Keep app bundles user-owned in ~/Applications","Always surface the 'Allow Anyway' guidance when quarantine stripping fails"],"tags":["rust","macos","gatekeeper","quarantine","xattr"],"backgroundTag":"macos-gatekeeper-quarantine","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}