{"record":{"id":"b8cd73817753f56f","repo":"chenhg5/cc-connect","slug":"backup-old-binary-w-b8cd73","errorCode":null,"errorMessage":"backup old binary: %w","messagePattern":"backup old binary: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/updater.go","lineNumber":279,"sourceCode":"\n\tif _, err := tmpFile.Write(newBinary); err != nil {\n\t\ttmpFile.Close()\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"write new binary: %w\", err)\n\t}\n\ttmpFile.Close()\n\n\tif err := os.Chmod(tmpPath, 0o755); err != nil {\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"chmod: %w\", err)\n\t}\n\n\toldPath := execPath + \".old\"\n\tos.Remove(oldPath)\n\n\tif err := os.Rename(execPath, oldPath); err != nil {\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"backup old binary: %w\", err)\n\t}\n\n\tif err := os.Rename(tmpPath, execPath); err != nil {\n\t\t// Try to restore\n\t\tif restoreErr := os.Rename(oldPath, execPath); restoreErr != nil {\n\t\t\tslog.Error(\"updater: failed to restore old binary after install failed\", \"error\", restoreErr)\n\t\t}\n\t\treturn fmt.Errorf(\"install new binary: %w\", err)\n\t}\n\n\t// Don't remove .old file on Linux - the running process may still need it\n\t// for os.Executable() to work correctly after restart.\n\t// The .old file will be overwritten on next update.\n\n\tslog.Info(\"updater: binary replaced successfully\", \"path\", execPath)\n\treturn nil\n}\n","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/updater.go#L261-L297","documentation":"replaceBinary preserves the running binary by renaming it to execPath + \".old\" before installing the new one; if os.Rename fails the temp file is removed and the error wrapped as \"backup old binary: %w\". Cross-device renames are the classic cause since rename cannot move across filesystems.","triggerScenarios":"SelfUpdate -> replaceBinary: os.Rename(execPath, oldPath) failed — the .old target is a directory, the binary's directory is on a filesystem where rename of the open executable is restricted (e.g. some Windows lock semantics in emulated environments), permission denied on the directory (write permission needed to rename within it), or the path disappeared after EvalSymlinks.","commonSituations":"A stale cc-connect.old directory (not file) left by a previous manual intervention; directory made read-only after the temp file was created via different privileges; SELinux policy blocking rename of a running executable; execPath deleted concurrently by a deploy script.","solutions":["Check for a leftover <binary>.old that is a directory or owned oddly (ls -la next to the binary) and remove/fix it, then retry.","Ensure the running user can write to the binary's directory (renaming requires directory write permission): sudo chown/chmod as needed.","Remove the stale .old file first: sudo rm -f /path/to/cc-connect.old — the updater also tries os.Remove(oldPath) before renaming, but only as the same (possibly unprivileged) user.","Check audit logs (SELinux/apparmor) if permission is denied unexpectedly and adjust policy.","Restart from the current binary and retry; if deploys delete binaries concurrently, serialize deploys with the updater."],"exampleFix":"# before\nls -la /opt/cc-connect\ndrwxr-xr-x cc-connect.old   # directory blocks rename\n# after\nsudo rm -rf /opt/cc-connect/cc-connect.old && sudo cc-connect update","handlingStrategy":"validation","validationCode":"oldPath := execPath + \".old\"\nif info, err := os.Lstat(oldPath); err == nil && info.IsDir() {\n    return fmt.Errorf(\"%s is a directory; remove it before updating\", oldPath)\n}\nos.Remove(oldPath) // pre-clean like the updater does","typeGuard":null,"tryCatchPattern":"if err := selfUpdate(); err != nil {\n    if strings.Contains(err.Error(), \"backup old binary\") {\n        // remove stale cc-connect.old, ensure dir write permission, retry\n    }\n}","preventionTips":["Never leave a cc-connect.old directory in the install path","Grant the run user write permission on the install directory","Serialize deploys with self-updates; don't delete binaries concurrently"],"tags":["filesystem","rename","self-update"],"backgroundTag":"file-already-exists","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}