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              int

View on GitHub (pinned to 4000b2338a)

Solutions

  1. 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.
  2. Ensure only one updater runs at a time — pause config-management/path watchers (systemd path units, Puppet/Chef) during update.
  3. 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).
  4. 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.
  5. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/406c01d5efede9d3. Report an issue: GitHub.