henrygd/beszel · error

failed to rename the current executable: %w

Error message

failed to rename the current executable: %w

What it means

During a self-update, ghupdate renames the currently running executable to a backup name before moving the newly downloaded binary into place. This error is wrapped from os.Rename when that rename of the old executable fails. The update is aborted before any replacement occurs, so the original binary is untouched.

Source

Thrown at internal/ghupdate/ghupdate.go:195

	oldExec, err := os.Executable()
	if err != nil {
		return false, err
	}
	renamedOldExec := oldExec + ".old"
	defer os.Remove(renamedOldExec)

	newExec := filepath.Join(extractDir, p.config.ArchiveExecutable)
	if _, err := os.Stat(newExec); err != nil {
		// try again with an .exe extension
		newExec = newExec + ".exe"
		if _, fallbackErr := os.Stat(newExec); fallbackErr != nil {
			return false, fmt.Errorf("the executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr)
		}
	}

	// rename the current executable
	if err := os.Rename(oldExec, renamedOldExec); err != nil {
		return false, fmt.Errorf("failed to rename the current executable: %w", err)
	}

	tryToRevertExecChanges := func() {
		if revertErr := os.Rename(renamedOldExec, oldExec); revertErr != nil {
			slog.Debug(
				"Failed to revert executable",
				slog.String("old", renamedOldExec),
				slog.String("new", oldExec),
				slog.String("error", revertErr.Error()),
			)
		}
	}

	// replace with the extracted binary
	if err := os.Rename(newExec, oldExec); err != nil {
		// If rename fails due to cross-device link, try copying instead
		if isCrossDeviceError(err) {
			if err := copyFile(newExec, oldExec); err != nil {

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Ensure the directory containing the executable is writable by the user running the update (chmod/chown or run with sufficient privileges).
  2. Close programs locking the executable (antivirus scans, editors, running instances) and retry the update.
  3. Move the binary to a local writable filesystem instead of a read-only or network mount.
  4. As a workaround, manually download the new release and replace the binary yourself, then re-run.

Example fix

// before: update run as unprivileged user against a root-owned binary
$ ./myapp update
// error: failed to rename the current executable: permission denied

// after: elevate or fix ownership first
$ sudo chown $(whoami) $(which myapp) && sudo chmod u+w $(dirname $(which myapp))
$ ./myapp update
Defensive patterns

Strategy: validation

Validate before calling

execPath, err := os.Executable()
if err != nil { return err }
dir := filepath.Dir(execPath)
if info, err := os.Stat(dir); err != nil || !info.IsDir() { return fmt.Errorf("bad exec dir") }
if f, err := os.OpenFile(filepath.Join(dir, ".write-test"), os.O_CREATE|os.O_WRONLY, 0o644); err != nil {
    return fmt.Errorf("directory not writable: %w", err)
} else { f.Close(); os.Remove(filepath.Join(dir, ".write-test")) }

Type guard

func canRenameIn(dir string) bool {
    fi, err := os.Stat(dir)
    return err == nil && fi.IsDir()
}

Try / catch

err := updater.Update(ctx, rel)
var updateErr *ghupdate.UpdateError
if errors.As(err, &updateErr) {
    log.Error("self-update failed, binary unchanged", "err", err)
} else if err != nil { log.Error("update error", "err", err) }

Prevention

When it happens

Trigger: Calling Update (via the update function) when os.Rename(oldExec, renamedOldExec) returns an error — e.g. the executable file is locked, permission on the containing directory denies rename, or the filesystem does not permit the operation.

Common situations: Running the binary from a read-only mount or a directory without write permission; antivirus or backup software holding a lock on the executable (especially on Windows); running as a non-root user while the binary is owned by root; the executable sitting on a filesystem like some NFS mounts where rename over a running binary fails.

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/4e0a6d48df4d3d58. Report an issue: GitHub.