chenhg5/cc-connect · error

remove unit: %w

Error message

remove unit: %w

What it means

Uninstall() wraps os.Remove failure for the unit file. Only unexpected errors (anything other than os.IsNotExist) are wrapped; disable and daemon-reload failures are merely logged. It means the unit file exists but could not be deleted.

Source

Thrown at daemon/systemd.go:96

		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)
	}

	unitPath := m.unitPath()
	if err := os.Remove(unitPath); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove unit: %w", err)
	}

	if _, err := runSystemctl(m.sysArgs("daemon-reload")...); err != nil {
		slog.Warn("systemd: daemon-reload failed", "error", err)
	}
	return nil
}

func (m *systemdManager) Start() error {
	out, err := runSystemctl(m.sysArgs("start", systemdServiceName)...)
	if err != nil {
		return fmt.Errorf("start: %s (%w)", out, err)
	}
	return nil
}

func (m *systemdManager) Stop() error {
	out, err := runSystemctl(m.sysArgs("stop", systemdServiceName)...)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Run the uninstall with sudo for system-level units.
  2. Check ownership of the unit file: `ls -l <unitPath>` and chown if needed.
  3. Delete manually and reload: `sudo rm <unitPath> && sudo systemctl daemon-reload`.
  4. Ensure the filesystem is writable.

Example fix

// before
cmd.Exec("cc-connect uninstall") // remove unit: permission denied
// after
cmd.Exec("sudo cc-connect uninstall")
Defensive patterns

Strategy: try-catch

Validate before calling

unitPath := mgr.unitPath()
if _, err := os.Stat(unitPath); os.IsNotExist(err) {
    return nil // nothing to uninstall
}
if err := syscall.Access(filepath.Dir(unitPath), 0o200); err != nil {
    return fmt.Errorf("no write permission on %s; rerun with sudo", filepath.Dir(unitPath))
}

Type guard

func unitRemovable(unitPath string) bool {
    return err := os.Remove(unitPath); err == nil || os.IsNotExist(err)
}

Try / catch

if err := mgr.Uninstall(); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {
        return fmt.Errorf("use sudo: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Uninstall when unitPath exists but the process lacks write permission on the unit directory, the unit file is owned by another user, or the filesystem is read-only.

Common situations: Uninstalling a root-installed unit as a normal user; unit directory permissions tightened by an admin; container/WSL with read-only /etc.

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/f2006fa0754335fa. Report an issue: GitHub.