gitbutlerapp/gitbutler · warning

Could not remove quarantine attribute

Error message

Could not remove quarantine attribute: {e} - macOS may show security warnings

What it means

On macOS the installer copies the staged .app to a temp location and runs remove_quarantine_recursive to strip the com.apple.quarantine xattr. If that fails, it warns with the error and notes macOS may show Gatekeeper security warnings, then continues the install and tells the user how to allow the app manually.

Solutions

  1. Continue with the install; when macOS blocks the app, open System Settings > Privacy & Security and click 'Allow Anyway' as the log suggests.
  2. Clear quarantine manually: xattr -dr com.apple.quarantine /Applications/<App>.app.
  3. Install onto the local APFS volume instead of a network mount.
  4. Run the installer from a normal terminal session with permission to modify extended attributes.
Defensive patterns

Strategy: fallback

Validate before calling

# Check quarantine status before/after install
import subprocess
def has_quarantine(path):
    r = subprocess.run(['xattr', path], capture_output=True)
    return b'com.apple.quarantine' in r.stdout

Try / catch

// Warn-and-continue pattern
if let Err(e) = remove_quarantine_recursive(&install_app_new) {
    warn("quarantine not removed: {e}; allow the app in System Settings > Privacy & Security");
}

Prevention

When it happens

Trigger: Raised in install_app (download_and_install_app) when remove_quarantine_recursive(&install_app_new) returns Err — typically xattr not available, the process lacks permission to change extended attributes, or the filesystem doesn't support xattrs (e.g. certain network/SMB volumes).

Common situations: Installing from a non-terminal context without permissions to run xattr; binaries not ad-hoc signed; installs onto SMB/NFS shares; hardened runtime environments; xattr command missing in minimal environments.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/7480042b359b653a. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-installer/src/install_macos.rs:104

            .map(|c| format!(" channel {}", c.display_name()))
            .unwrap_or_default(),
        install_app.display()
    ));

    // Clean up any leftover temp files from previous failed installations
    let _ = fs::remove_dir_all(&install_app_new);
    let _ = fs::remove_dir_all(&install_app_backup);

    // Create Applications directory if it doesn't exist
    fs::create_dir_all(home_dir.join("Applications"))?;

    // Install to temporary location first
    info("Installing to temporary location...");
    copy_dir_all(app_dir, &install_app_new)?;

    // Remove macOS quarantine attribute
    if let Err(e) = remove_quarantine_recursive(&install_app_new) {
        warn(&format!(
            "Could not remove quarantine attribute: {e} - macOS may show security warnings"
        ));
        info("If macOS blocks the app, go to System Settings > Privacy & Security and allow it");
    }

    // Create bin directory
    let bin_dir = home_dir.join(".local/bin");
    fs::create_dir_all(&bin_dir)?;

    // Check for existing 'but' and detect channel switching
    let but_symlink = bin_dir.join("but");
    let but_new = bin_dir.join("but.new");

    if but_symlink.exists() && !but_symlink.is_symlink() {
        // 'but' exists but is not a symlink
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()

View on GitHub (pinned to 58e5313667)