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

  1. Run as the same user (or root) that owns the existing unit file.
  2. Remove the stale unit first: `sudo rm /etc/systemd/system/cc-connect.service`, then reinstall.
  3. Check ownership: `ls -l <unitPath>` and `sudo chown` if needed.
  4. 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

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


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