{"record":{"id":"406c01d5efede9d3","repo":"chenhg5/cc-connect","slug":"install-new-binary-w-406c01","errorCode":null,"errorMessage":"install new binary: %w","messagePattern":"install new binary: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/updater.go","lineNumber":287,"sourceCode":"\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\n// --- semver comparison ---\n\nvar semverRe = regexp.MustCompile(`^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-(.+))?$`)\n\ntype semver struct {\n\tmajor, minor, patch int\n\tpre                 string\n\tpreNum              int","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/updater.go#L269-L305","documentation":"The final step swaps the staged binary into place: os.Rename(tmpPath, execPath). If that fails, replaceBinary attempts to restore the old binary from .old and returns \"install new binary: %w\" (logging a slog.Error if even the restore fails). The running process is unaffected, but the on-disk binary may be the restored old one.","triggerScenarios":"SelfUpdate -> replaceBinary: os.Rename(tmpPath, execPath) failed after the backup rename succeeded — target path recreated concurrently by another process, cross-device rename (tmp and exec ended up on different filesystems after a mount change), permission revocation mid-update, or the .old restore also failing, leaving no binary at execPath (restored file logged via slog.Error).","commonSituations":"Deployment tooling (systemd path unit, config management) recreating the binary path during the update window; admin unmounted/moved the volume between steps; concurrent cc-connect update runs racing each other; SELinux denying rename over a labeled executable.","solutions":["Check whether cc-connect.old still exists and execPath is missing; if so restore manually: mv /path/cc-connect.old /path/cc-connect (or sudo), then restart and retry the update.","Ensure only one updater runs at a time — pause config-management/path watchers (systemd path units, Puppet/Chef) during update.","Verify the binary directory permissions and mounts did not change mid-update; keep tmp and binary on the same filesystem (the updater stages in the same dir by design, so check for mount moves).","Inspect slog output for 'failed to restore old binary after install failed' — if present, the .old backup is your only recovery copy; handle it with care before deleting anything.","Retry the update from a stable environment; the temp file may remain if the failure happened before cleanup — remove stale cc-connect-update-* files first."],"exampleFix":"# before: execPath missing, old backup present\nls /opt/cc-connect  # only cc-connect.old\n# after\nsudo mv /opt/cc-connect/cc-connect.old /opt/cc-connect/cc-connect\nsudo systemctl restart cc-connect && sudo cc-connect update","handlingStrategy":"try-catch","validationCode":"// before updating: ensure nothing else manages the binary path\nif pid, err := os.FindProcess(1); err == nil && isSystemdPathUnitWatching(execPath) {\n    return fmt.Errorf(\"stop path watcher before self-update\")\n}\nif _, err := os.Stat(execPath + \".old\"); err == nil {\n    return fmt.Errorf(\"stale .old backup exists; clean up first\")\n}","typeGuard":null,"tryCatchPattern":"if err := selfUpdate(); err != nil {\n    if strings.Contains(err.Error(), \"install new binary\") {\n        // check for cc-connect.old and restore:\n        os.Rename(execPath+\".old\", execPath)\n        // then retry update after resolving the contention\n    }\n}","preventionTips":["Run only one updater at a time (use a lockfile)","Pause systemd path units / config management during updates","Keep tmp staging and binary on the same filesystem (default behavior)","Never delete cc-connect.old before confirming the new binary runs"],"tags":["filesystem","rename","atomic-update","self-update"],"backgroundTag":"file-write-failed","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"}