{"record":{"id":"a5efd850b7dd2876","repo":"chenhg5/cc-connect","slug":"create-log-dir-w","errorCode":null,"errorMessage":"create log dir: %w","messagePattern":"create log dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"daemon/launchd.go","lineNumber":49,"sourceCode":"// independently of login sessions, so no \"linger\" warning is needed.\nfunc CheckLinger() (enabled bool, user string) {\n\treturn true, \"\"\n}\n\nfunc newPlatformManager() (Manager, error) {\n\treturn &launchdManager{}, nil\n}\n\nfunc (*launchdManager) Platform() string { return \"launchd\" }\n\nfunc (m *launchdManager) Install(cfg Config) error {\n\tplistPath := launchdPlistPath()\n\n\tif err := os.MkdirAll(filepath.Dir(plistPath), 0755); err != nil {\n\t\treturn fmt.Errorf(\"create LaunchAgents 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\t// Unload existing service first (ignore errors) so we do not leave a stale\n\t// job behind when switching between GUI and headless sessions.\n\tbootoutLaunchdTargets()\n\n\tplist := buildPlist(cfg)\n\t// 0600: plist may contain captured secret values (config.toml ${ENV}\n\t// placeholders and any EnvDiscoverer extension output). User-only\n\t// LaunchAgents path; root can still read but that is the user's own\n\t// machine boundary. os.WriteFile only applies perm on create, so\n\t// Chmod afterwards is required to harden reinstalls of files that\n\t// pre-existed at 0644 from earlier cc-connect versions.\n\tif err := os.WriteFile(plistPath, []byte(plist), 0600); err != nil {\n\t\treturn fmt.Errorf(\"write plist: %w\", err)\n\t}\n\tif err := os.Chmod(plistPath, 0600); err != nil {\n\t\treturn fmt.Errorf(\"chmod plist: %w\", err)","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/daemon/launchd.go#L31-L67","documentation":"launchdManager.Install also ensures the directory for the configured LogFile exists (creating it with 0755) so launchd's stdout/stderr redirection does not fail. Failure to create that directory is wrapped as 'create log dir:'. The wrapped cause contains the underlying OS error.","triggerScenarios":"Calling daemon.Install (launchd) with Config.LogFile whose parent directory cannot be created — e.g. LogFile points into a non-writable path like /var/log on macOS (SIP-protected) or a path component that is a regular file.","commonSituations":"Config copied from a Linux systemd setup with LogFile=/var/log/cc-connect.log; HOME misconfigured so ~/Library/Logs cannot be created; a file named 'Logs' blocking the directory path.","solutions":["Set LogFile to a user-writable location (default ~/Library/Logs/...) in the daemon config","Create the log directory manually and verify permissions","Ensure no regular file occupies a path component of the LogFile directory","Check the wrapped %w cause (EACCES/ENOTDIR/etc.) for the exact OS reason"],"exampleFix":"// before\nlogFile = \"/var/log/cc-connect.log\"  // SIP-protected\n// after\nlogFile = \"/Users/me/Library/Logs/cc-connect.log\"","handlingStrategy":"validation","validationCode":"logDir := filepath.Dir(cfg.LogFile)\nif st, err := os.Stat(logDir); err == nil && !st.IsDir() {\n    return fmt.Errorf(\"log dir path %s is a file\", logDir)\n}\nif err := os.MkdirAll(logDir, 0755); err != nil {\n    return fmt.Errorf(\"cannot prepare log dir: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := daemon.Install(cfg); err != nil {\n    if strings.Contains(err.Error(), \"create log dir\") {\n        // fix cfg.LogFile to a user-writable path and retry\n    }\n    return err\n}","preventionTips":["Configure LogFile under the user's home (~/Library/Logs)","Never point LogFile at SIP-protected paths like /var/log on macOS","Verify no regular file occupies a log-dir path component"],"tags":["filesystem","launchd","install","log"],"backgroundTag":"mkdir-permission-denied","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"}