{"record":{"id":"4777d906de1212d5","repo":"henrygd/beszel","slug":"failed-replacing-the-executable-w","errorCode":null,"errorMessage":"failed replacing the executable: %w","messagePattern":"failed replacing the executable: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ghupdate/ghupdate.go","lineNumber":215,"sourceCode":"\n\ttryToRevertExecChanges := func() {\n\t\tif revertErr := os.Rename(renamedOldExec, oldExec); revertErr != nil {\n\t\t\tslog.Debug(\n\t\t\t\t\"Failed to revert executable\",\n\t\t\t\tslog.String(\"old\", renamedOldExec),\n\t\t\t\tslog.String(\"new\", oldExec),\n\t\t\t\tslog.String(\"error\", revertErr.Error()),\n\t\t\t)\n\t\t}\n\t}\n\n\t// replace with the extracted binary\n\tif err := os.Rename(newExec, oldExec); err != nil {\n\t\t// If rename fails due to cross-device link, try copying instead\n\t\tif isCrossDeviceError(err) {\n\t\t\tif err := copyFile(newExec, oldExec); err != nil {\n\t\t\t\ttryToRevertExecChanges()\n\t\t\t\treturn false, fmt.Errorf(\"failed replacing the executable: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\ttryToRevertExecChanges()\n\t\t\treturn false, fmt.Errorf(\"failed replacing the executable: %w\", err)\n\t\t}\n\t}\n\n\tColorPrint(colorGray, \"---\")\n\tColorPrint(ColorGreen, \"Update completed successfully!\")\n\n\t// print the release notes\n\tif latest.Body != \"\" {\n\t\tfmt.Print(\"\\n\")\n\t\treleaseNotes := strings.TrimSpace(strings.Replace(latest.Body, \"> _To update the prebuilt executable you can run `./\"+p.config.ArchiveExecutable+\" update`._\", \"\", 1))\n\t\tColorPrint(colorCyan, releaseNotes)\n\t\tfmt.Print(\"\\n\")\n\t}\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/internal/ghupdate/ghupdate.go#L197-L233","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: a leftover directory blocks the target path\n$ ls -la $(which myapp)   # myapp is a directory\n// error: failed replacing the executable: file exists\n\n// after: remove the conflicting entry and retry\n$ rm -rf $(which myapp) && ./myapp update","handlingStrategy":"try-catch","validationCode":"execPath, _ := os.Executable()\nif fi, err := os.Lstat(execPath); err == nil && fi.IsDir() {\n    return fmt.Errorf(\"%s is a directory; remove it before updating\", execPath)\n}","typeGuard":"func isCrossDeviceError(err error) bool {\n    var le *os.LinkError\n    return errors.As(err, &le) && errors.Is(le.Err, syscall.EXDEV)\n}","tryCatchPattern":"if err := updater.Update(ctx, rel); err != nil {\n    if strings.Contains(err.Error(), \"failed replacing the executable\") {\n        log.Warn(\"swap failed; original binary restored, retry or replace manually\", \"err\", err)\n        return retryUpdate()\n    }\n    return err\n}","preventionTips":["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."],"tags":["self-update","filesystem","rename","permissions"],"backgroundTag":"executable-rename-failed","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}