{"record":{"id":"4a8c878f31da9583","repo":"chenhg5/cc-connect","slug":"write-unit-file-w","errorCode":null,"errorMessage":"write unit file: %w","messagePattern":"write unit file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"daemon/systemd.go","lineNumber":70,"sourceCode":"\tunitPath := m.unitPath()\n\n\tif err := os.MkdirAll(filepath.Dir(unitPath), 0755); err != nil {\n\t\treturn fmt.Errorf(\"create systemd dir: %w\", err)\n\t}\n\tif err := os.MkdirAll(filepath.Dir(cfg.LogFile), 0755); err != nil {\n\t\treturn fmt.Errorf(\"create log dir: %w\", err)\n\t}\n\n\tunit := m.buildUnit(cfg)\n\t// 0600: unit file may contain captured secret values (config.toml ${ENV}\n\t// placeholders and any EnvDiscoverer extension output). For system-level\n\t// units (/etc/systemd/system/) the file is owned by root and remains\n\t// readable by root only; for user-level units under\n\t// ~/.config/systemd/user it remains owner-only. WriteFile only applies\n\t// perm on create, so Chmod afterwards is required to harden reinstalls\n\t// of pre-existing 0644 units from earlier cc-connect versions.\n\tif err := os.WriteFile(unitPath, []byte(unit), 0600); err != nil {\n\t\treturn fmt.Errorf(\"write unit file: %w\", err)\n\t}\n\tif err := os.Chmod(unitPath, 0600); err != nil {\n\t\treturn fmt.Errorf(\"chmod unit file: %w\", err)\n\t}\n\n\tfor _, cmdArgs := range [][]string{\n\t\tm.sysArgs(\"daemon-reload\"),\n\t\tm.sysArgs(\"enable\", systemdServiceName),\n\t\tm.sysArgs(\"restart\", systemdServiceName),\n\t} {\n\t\tif out, err := runSystemctl(cmdArgs...); err != nil {\n\t\t\treturn fmt.Errorf(\"systemctl %s: %s (%w)\", strings.Join(cmdArgs, \" \"), out, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/daemon/systemd.go#L52-L88","documentation":"Install() wraps os.WriteFile failure when writing the generated .service unit to unitPath. The unit may contain secrets, so it is written 0600; a failed write means the unit could not be persisted (permissions, read-only fs, or the path is a directory).","triggerScenarios":"Calling Install when the unit directory exists but is not writable (system install without root), the unit path is a directory, or the disk is full/read-only.","commonSituations":"Non-root user running system install; SELinux/apparmor denying writes to /etc/systemd/system; unit left in a corrupted state by a previous failed install.","solutions":["Run with sudo for system-level units.","Ensure unitPath is not a directory: `ls -ld /etc/systemd/system/cc-connect.service`.","Check free disk space and mount status (`df -h`, `mount`).","Verify no security module (SELinux) denies the write; check `ausearch -m avc`."],"exampleFix":"// before\ncmd.Exec(\"cc-connect install\") // as non-root: write unit file: permission denied\n// after\ncmd.Exec(\"sudo cc-connect install\")","handlingStrategy":"validation","validationCode":"unitPath := mgr.unitPath()\nif fi, err := os.Stat(unitPath); err == nil && fi.IsDir() {\n    return fmt.Errorf(\"%s is a directory\", unitPath)\n}\nprobe, err := os.CreateTemp(filepath.Dir(unitPath), \".ccprobe\")\nif err != nil {\n    return fmt.Errorf(\"unit dir not writable: %w\", err)\n}\nprobe.Close(); os.Remove(probe.Name())","typeGuard":"func canCreateFile(dir string) bool {\n    f, err := os.CreateTemp(dir, \".probe\")\n    if err != nil { return false }\n    f.Close(); os.Remove(f.Name())\n    return true\n}","tryCatchPattern":"if err := mgr.Install(cfg); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {\n        return fmt.Errorf(\"run with sudo: %w\", err)\n    }\n    return err\n}","preventionTips":["Verify write access to /etc/systemd/system before system installs.","Ensure no directory occupies the unit file path.","Check disk space (df -h) on machines with small root volumes.","On SELinux systems, check AVC denials if writes to /etc fail."],"tags":["go","systemd","filesystem","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}