henrygd/beszel · error
failed replacing the executable: %w
Error message
failed replacing the executable: %w
What it means
After renaming the old executable aside, update tries os.Rename(newExec, oldExec) to put the downloaded binary in place. If that rename fails, it retries via copyFile when the error is a cross-device link (EXDEV); this error is returned when the non-cross-device rename fails. tryToRevertExecChanges has already restored the original executable name, so the installation is left intact but not updated.
Source
Thrown at internal/ghupdate/ghupdate.go:215
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 {
tryToRevertExecChanges()
return false, fmt.Errorf("failed replacing the executable: %w", err)
}
} else {
tryToRevertExecChanges()
return false, fmt.Errorf("failed replacing the executable: %w", err)
}
}
ColorPrint(colorGray, "---")
ColorPrint(ColorGreen, "Update completed successfully!")
// print the release notes
if latest.Body != "" {
fmt.Print("\n")
releaseNotes := strings.TrimSpace(strings.Replace(latest.Body, "> _To update the prebuilt executable you can run `./"+p.config.ArchiveExecutable+" update`._", "", 1))
ColorPrint(colorCyan, releaseNotes)
fmt.Print("\n")
}
View on GitHub (pinned to b38fb7dafa)
Solutions
- Check that no file or directory at the executable's path conflicts (remove a stray directory with the binary's name).
- Re-run the update; transient locks (AV scanners) usually clear on retry.
- Verify no mandatory access control (SELinux/AppArmor) is denying the rename; check audit logs.
- Manually replace the binary: download the release, move it over the old path, and chmod +x.
Example fix
// before: a leftover directory blocks the target path $ ls -la $(which myapp) # myapp is a directory // error: failed replacing the executable: file exists // after: remove the conflicting entry and retry $ rm -rf $(which myapp) && ./myapp update
Defensive patterns
Strategy: try-catch
Validate before calling
execPath, _ := os.Executable()
if fi, err := os.Lstat(execPath); err == nil && fi.IsDir() {
return fmt.Errorf("%s is a directory; remove it before updating", execPath)
} Type guard
func isCrossDeviceError(err error) bool {
var le *os.LinkError
return errors.As(err, &le) && errors.Is(le.Err, syscall.EXDEV)
} Try / catch
if err := updater.Update(ctx, rel); err != nil {
if strings.Contains(err.Error(), "failed replacing the executable") {
log.Warn("swap failed; original binary restored, retry or replace manually", "err", err)
return retryUpdate()
}
return err
} Prevention
- Never create files/dirs named like the executable next to it.
- Re-run the update after transient locks clear — the library self-reverts on this failure.
- Check audit logs (SELinux/AppArmor) if failures repeat on hardened hosts.
- Stop dependent processes holding the binary open during update windows.
When it happens
Trigger: Calling Update when os.Rename(newExec, oldExec) fails with any error other than a cross-device link — e.g. the old path was recreated as a directory, the target is locked, or a permission error occurred on the target path.
Common situations: A directory named exactly like the executable exists at the target path; another process recreated/locked the executable between the two renames; SELinux/AppArmor denials blocking the rename operation; disk full preventing metadata operations in edge setups.
Related errors
- failed to rename the current executable: %w
- failed to create update directory: %w
- data directory not found
- chmod: %w
- rename: %w
AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31).
Data as JSON: /api/errors/4777d906de1212d5.
Report an issue: GitHub.