chenhg5/cc-connect · error

resolve symlinks: %w

Error message

resolve symlinks: %w

What it means

After obtaining the executable path, replaceBinary calls filepath.EvalSymlinks to resolve indirection; failure is wrapped as "resolve symlinks: %w". The updater must write next to the real binary, so unresolved or broken symlinks abort the update.

Source

Thrown at core/updater.go:252

			rc, err := f.Open()
			if err != nil {
				return nil, err
			}
			defer rc.Close()
			return io.ReadAll(rc)
		}
	}
	return nil, fmt.Errorf("cc-connect binary not found in zip archive")
}

func replaceBinary(newBinary []byte) error {
	execPath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("get executable path: %w", err)
	}
	execPath, err = filepath.EvalSymlinks(execPath)
	if err != nil {
		return fmt.Errorf("resolve symlinks: %w", err)
	}

	dir := filepath.Dir(execPath)
	tmpFile, err := os.CreateTemp(dir, "cc-connect-update-*")
	if err != nil {
		return fmt.Errorf("create temp file: %w", err)
	}
	tmpPath := tmpFile.Name()

	if _, err := tmpFile.Write(newBinary); err != nil {
		tmpFile.Close()
		os.Remove(tmpPath)
		return fmt.Errorf("write new binary: %w", err)
	}
	tmpFile.Close()

	if err := os.Chmod(tmpPath, 0o755); err != nil {
		os.Remove(tmpPath)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the symlink chain: `ls -l $(which cc-connect)` and `readlink -f` it; recreate or fix the broken link, then retry the update.
  2. Restart the process from a valid path so EvalSymlinks succeeds (broken chain usually only affects the already-running instance).
  3. Reinstall the binary directly at the real target location and update from there, bypassing the broken symlink.
  4. Check directory permissions (execute bit on every path component) if the error is permission-related.
  5. Avoid symlink loops; point the stable link to a concrete versioned directory that exists.

Example fix

# before: dangling symlink
lrwxr-xr-x cc-connect -> /opt/cc-connect/v0.8/cc-connect  # v0.8 deleted
# after
ln -sfn /opt/cc-connect/v0.9/cc-connect /usr/local/bin/cc-connect
Defensive patterns

Strategy: validation

Validate before calling

p, _ := os.Executable()
real, err := filepath.EvalSymlinks(p)
if err != nil {
    return fmt.Errorf("broken install path: %v", err)
}
if _, err := os.Stat(real); err != nil {
    return fmt.Errorf("symlink target missing: %v", err)
}

Try / catch

if err := selfUpdate(); err != nil {
    if strings.Contains(err.Error(), "resolve symlinks") {
        // repair symlink chain (readlink -f) then reinstall/restart
    }
}

Prevention

When it happens

Trigger: SelfUpdate -> replaceBinary: EvalSymlinks returned an error because a component of the executable's path no longer exists (the binary or a parent dir was replaced/removed after start), a symlink loop exists, or a path component lacks search permission.

Common situations: Binary installed via a symlink chain (e.g. /usr/local/bin/cc-connect -> /opt/app/current/cc-connect) where a deploy later broke or removed the link while the old process still runs;管理者 removed the versioned target directory; permission-restricted directories after a security hardening change.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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