gitbutlerapp/gitbutler · error

Failed to create symlink

Error message

Failed to create symlink: {e} - attempting to restore backup

What it means

After moving the new app bundle into place, install_app attempts unix_fs::symlink(final_target, but_symlink). If symlink creation fails, the installer warns with the OS error and enters rollback: it restores the backed-up previous installation (install_app_backup) if present, otherwise bails. The message means the atomic install could not finish and the previous state is being recovered.

Solutions

  1. Re-run the installer with elevated privileges (sudo) or point it at a user-writable bin directory on PATH.
  2. Check what occupies the symlink path: `ls -la $(dirname $(which but))/but` and remove any conflicting directory/file.
  3. Verify free disk space and that the app bundle target (Contents/MacOS/gitbutler-tauri) exists in the installed .app.
  4. Check the printed error after 'Failed to create symlink:' for the underlying errno and fix accordingly (e.g. chown the bin dir).

Example fix

# before
$ ./gitbutler-installer   # fails: permission denied on /usr/local/bin
# after
$ sudo ./gitbutler-installer   # or set install dir to ~/bin (on PATH)
Defensive patterns

Strategy: validation

Validate before calling

# Verify the bin dir is writable and the path is not occupied before installing.
test -w /usr/local/bin || echo 'need sudo or a different install dir'
[ -d /usr/local/bin/but ] && echo "conflict: /usr/local/bin/but is a directory"

Try / catch

// Wrap the installer run; treat non-zero exit as rollback failure candidate.
const res = spawnSync(installer, { stdio: 'inherit' });
if (res.status !== 0) {
  console.error('Install failed; check rollback note and permissions on the target bin dir.');
}

Prevention

When it happens

Trigger: download_and_install_app -> install_app on macOS when creating the 'but' symlink fails, typically because the bin directory is not writable (permissions, SIP-protected paths, the path is a directory rather than a symlink) or the target file does not exist.

Common situations: Installing into /usr/local/bin without sudo on a locked-down Mac; corporate MDM policies blocking symlink creation in PATH dirs; a leftover 'but' directory occupying the symlink path; disk full.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

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

            bail!(
                "Failed to install new version: {e}. Previous installation restored successfully."
            );
        }
        // No backup to restore, just fail
        bail!("Failed to move new installation into place: {e}");
    }

    // Update the symlink to point to the new installation
    let final_target = install_app.join("Contents/MacOS/gitbutler-tauri");
    let _ = fs::remove_file(&but_symlink);

    // Try to create symlink and verify - if either fails, rollback
    let symlink_result = unix_fs::symlink(&final_target, &but_symlink);
    let _ = fs::remove_file(&but_new);

    if let Err(e) = symlink_result {
        // Symlink creation failed - rollback to backup
        warn(&format!(
            "Failed to create symlink: {e} - attempting to restore backup"
        ));
        if install_app_backup.exists() {
            if let Err(remove_err) = fs::remove_dir_all(&install_app) {
                bail!(
                    "Failed to create symlink and failed to remove new installation during rollback: {}. Backup at: {}",
                    remove_err,
                    install_app_backup.display()
                );
            }
            fs::rename(&install_app_backup, &install_app)?;

            let restored_target = install_app.join("Contents/MacOS/gitbutler-tauri");
            let _ = fs::remove_file(&but_symlink);
            let _ = unix_fs::symlink(&restored_target, &but_symlink);

            bail!("Failed to create symlink: {e}. Previous installation was restored.");
        } else {

View on GitHub (pinned to 58e5313667)