chenhg5/cc-connect · error
chmod unit file: %w
Error message
chmod unit file: %w
What it means
Install() wraps os.Chmod failure when hardening the unit file to 0600 after writing it. WriteFile only applies permissions on create, so Chmod is required to tighten pre-existing 0644 units from older cc-connect versions; if Chmod fails the unit would be left world-readable despite containing secrets.
Source
Thrown at daemon/systemd.go:73
return fmt.Errorf("create systemd dir: %w", err)
}
if err := os.MkdirAll(filepath.Dir(cfg.LogFile), 0755); err != nil {
return fmt.Errorf("create log dir: %w", err)
}
unit := m.buildUnit(cfg)
// 0600: unit file may contain captured secret values (config.toml ${ENV}
// placeholders and any EnvDiscoverer extension output). For system-level
// units (/etc/systemd/system/) the file is owned by root and remains
// readable by root only; for user-level units under
// ~/.config/systemd/user it remains owner-only. WriteFile only applies
// perm on create, so Chmod afterwards is required to harden reinstalls
// of pre-existing 0644 units from earlier cc-connect versions.
if err := os.WriteFile(unitPath, []byte(unit), 0600); err != nil {
return fmt.Errorf("write unit file: %w", err)
}
if err := os.Chmod(unitPath, 0600); err != nil {
return fmt.Errorf("chmod unit file: %w", err)
}
for _, cmdArgs := range [][]string{
m.sysArgs("daemon-reload"),
m.sysArgs("enable", systemdServiceName),
m.sysArgs("restart", systemdServiceName),
} {
if out, err := runSystemctl(cmdArgs...); err != nil {
return fmt.Errorf("systemctl %s: %s (%w)", strings.Join(cmdArgs, " "), out, err)
}
}
return nil
}
func (m *systemdManager) Uninstall() error {
if _, err := runSystemctl(m.sysArgs("disable", "--now", systemdServiceName)...); err != nil {
slog.Warn("systemd: disable failed", "error", err)View on GitHub (pinned to 4000b2338a)
Solutions
- Run as the same user (or root) that owns the existing unit file.
- Remove the stale unit first: `sudo rm /etc/systemd/system/cc-connect.service`, then reinstall.
- Check ownership: `ls -l <unitPath>` and `sudo chown` if needed.
- Verify the filesystem supports permission changes.
Example fix
// before
mgr.Install(cfg) // chmod unit file: operation not permitted (root-owned unit)
// after
exec.Command("sudo", "rm", "/etc/systemd/system/cc-connect.service").Run()
mgr.Install(cfg) Defensive patterns
Strategy: validation
Validate before calling
unitPath := mgr.unitPath()
if fi, err := os.Stat(unitPath); err == nil {
if st, err := os.Stat(unitPath); err == nil && st.Uid() != uint32(os.Getuid()) && os.Getuid() != 0 {
return fmt.Errorf("existing unit owned by uid %d; rerun as root", st.Uid())
}
} Type guard
func ownsFile(path string) bool {
fi, err := os.Stat(path)
if err != nil { return true }
return fi.Sys().(*syscall.Stat_t).Uid == uint32(os.Getuid())
} Try / catch
if err := mgr.Install(cfg); err != nil {
if strings.Contains(err.Error(), "chmod unit file") {
_ = os.Remove(mgr.unitPath()) // drop stale unit and retry as correct user
return mgr.Install(cfg)
}
return err
} Prevention
- Always reinstall with the same privilege level used originally (root vs user).
- Delete stale units before switching install modes.
- Avoid unit directories on filesystems without chmod support (WSL /mnt/c, FAT).
- Verify resulting permissions after install: stat -c %a <unitPath>.
When it happens
Trigger: Calling Install over an existing unit file whose ownership prevents Chmod (file owned by another user), or on a filesystem that does not support chmod (some network/Windows mounts).
Common situations: Previous unit installed as root, now reinstalling as a different user; reinstalling on a WSL mount or FAT/NTFS volume; stale root-owned unit from an old version.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- create systemd dir: %w
- write unit file: %w
- remove unit: %w
- read existing Agy hooks %s: %w
- kimi: read sessions dir: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/cffe2805c00006ae.
Report an issue: GitHub.