chenhg5/cc-connect · critical
install new binary: %w
Error message
install new binary: %w
What it means
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.
Source
Thrown at core/updater.go:287
if err := os.Chmod(tmpPath, 0o755); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("chmod: %w", err)
}
oldPath := execPath + ".old"
os.Remove(oldPath)
if err := os.Rename(execPath, oldPath); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("backup old binary: %w", err)
}
if err := os.Rename(tmpPath, execPath); err != nil {
// Try to restore
if restoreErr := os.Rename(oldPath, execPath); restoreErr != nil {
slog.Error("updater: failed to restore old binary after install failed", "error", restoreErr)
}
return fmt.Errorf("install new binary: %w", err)
}
// Don't remove .old file on Linux - the running process may still need it
// for os.Executable() to work correctly after restart.
// The .old file will be overwritten on next update.
slog.Info("updater: binary replaced successfully", "path", execPath)
return nil
}
// --- semver comparison ---
var semverRe = regexp.MustCompile(`^v?(\d+)\.(\d+)\.(\d+)(?:-(.+))?$`)
type semver struct {
major, minor, patch int
pre string
preNum intView on GitHub (pinned to 4000b2338a)
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.
Example fix
# before: execPath missing, old backup present ls /opt/cc-connect # only cc-connect.old # after sudo mv /opt/cc-connect/cc-connect.old /opt/cc-connect/cc-connect sudo systemctl restart cc-connect && sudo cc-connect update
Defensive patterns
Strategy: try-catch
Validate before calling
// before updating: ensure nothing else manages the binary path
if pid, err := os.FindProcess(1); err == nil && isSystemdPathUnitWatching(execPath) {
return fmt.Errorf("stop path watcher before self-update")
}
if _, err := os.Stat(execPath + ".old"); err == nil {
return fmt.Errorf("stale .old backup exists; clean up first")
} Try / catch
if err := selfUpdate(); err != nil {
if strings.Contains(err.Error(), "install new binary") {
// check for cc-connect.old and restore:
os.Rename(execPath+".old", execPath)
// then retry update after resolving the contention
}
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- backup old binary: %w
- get executable path: %w
- resolve symlinks: %w
- create temp file: %w
- write new binary: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/406c01d5efede9d3.
Report an issue: GitHub.