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
- Inspect the symlink chain: `ls -l $(which cc-connect)` and `readlink -f` it; recreate or fix the broken link, then retry the update.
- Restart the process from a valid path so EvalSymlinks succeeds (broken chain usually only affects the already-running instance).
- Reinstall the binary directly at the real target location and update from there, bypassing the broken symlink.
- Check directory permissions (execute bit on every path component) if the error is permission-related.
- 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
- Verify `readlink -f $(which cc-connect)` resolves before updating
- Recreate symlink chains atomically (ln -sfn) on deploy
- Keep path components executable (searchable) by the run user
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.