{"record":{"id":"4b5dc4d61f0e18ed","repo":"chenhg5/cc-connect","slug":"create-launchagents-dir-w","errorCode":null,"errorMessage":"create LaunchAgents dir: %w","messagePattern":"create LaunchAgents dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"daemon/launchd.go","lineNumber":46,"sourceCode":"type launchdManager struct{}\n\n// CheckLinger always returns true on macOS: launchd user agents persist\n// 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)","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/daemon/launchd.go#L28-L64","documentation":"launchdManager.Install creates the user's ~/Library/LaunchAgents directory before writing the plist. If os.MkdirAll on that directory fails, the error is wrapped with 'create LaunchAgents dir:' to identify the failing step. This typically indicates a filesystem-level problem (permissions, path is a file, disk full).","triggerScenarios":"Calling daemon.Install (launchd) when filepath.Dir(launchdPlistPath()) cannot be created with mode 0755 — e.g. ~/Library exists as a regular file, or a component of the path denies write access.","commonSituations":"Running under an account with a broken/restricted HOME; MDM or security tools locking ~/Library; a stray file named LaunchAgents at the expected path; read-only home volume.","solutions":["Check that ~/Library exists as a directory and is writable: ls -ld ~/Library ~/Library/LaunchAgents","Remove/rename any non-directory named LaunchAgents at the plist path","Run the install as the actual user (not root) so the plist lands in the correct LaunchAgents directory","Inspect the wrapped %w cause in the error for the underlying OS reason"],"exampleFix":"// before\n$ cc-connect daemon install  // HOME read-only\n// after\n$ chmod u+w ~/Library && cc-connect daemon install","handlingStrategy":"try-catch","validationCode":"dir := filepath.Dir(plistPath)\nif st, err := os.Stat(dir); err != nil {\n    // parent missing — verify you can create it\n} else if !st.IsDir() {\n    return fmt.Errorf(\"%s exists but is not a directory\", dir)\n}","typeGuard":null,"tryCatchPattern":"if err := daemon.Install(cfg); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        log.Printf(\"launchd install failed at %s: %v\", pe.Path, pe.Err)\n    }\n    return err\n}","preventionTips":["Run daemon install as the regular user with a writable HOME","Ensure ~/Library/LaunchAgents exists as a directory, not a file","Avoid MDM/security policies blocking writes under ~/Library"],"tags":["filesystem","launchd","install","permissions"],"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"}