{"record":{"id":"467387e6bbc5f34b","repo":"chenhg5/cc-connect","slug":"read-agy-config-directory-s-w","errorCode":null,"errorMessage":"read Agy config directory %s: %w","messagePattern":"read Agy config directory (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/antigravity/permission_bridge.go","lineNumber":172,"sourceCode":"\n\tdata, err := json.MarshalIndent(hooks, \"\", \"  \")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"marshal Agy hooks overlay: %w\", err)\n\t}\n\tdata = append(data, '\\n')\n\tif err := os.WriteFile(filepath.Join(overlayConfigDir, \"hooks.json\"), data, 0o600); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write Agy hooks overlay: %w\", err)\n\t}\n\treturn overlayConfigRoot, nil\n}\n\nfunc mirrorDirectoryEntries(sourceDir, targetDir string, skip map[string]bool) error {\n\tentries, err := os.ReadDir(sourceDir)\n\tif os.IsNotExist(err) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read Agy config directory %s: %w\", sourceDir, err)\n\t}\n\tfor _, entry := range entries {\n\t\tif skip[entry.Name()] {\n\t\t\tcontinue\n\t\t}\n\t\tsource := filepath.Join(sourceDir, entry.Name())\n\t\ttarget := filepath.Join(targetDir, entry.Name())\n\t\tif err := os.Symlink(source, target); err != nil {\n\t\t\treturn fmt.Errorf(\"link Agy config %s: %w\", source, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc shellQuote(value string) string {\n\treturn \"'\" + strings.ReplaceAll(value, \"'\", \"'\\\"'\\\"'\") + \"'\"\n}\n","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/antigravity/permission_bridge.go#L154-L190","documentation":"mirrorDirectoryEntries wraps any error from os.ReadDir on the antigravity ('Agy') source config directory. It is thrown when the directory exists but cannot be read (permissions, I/O error, or source is a file, not a directory). A non-existent directory is intentionally treated as success (returns nil), so this error indicates the directory exists but is unreadable.","triggerScenarios":"os.ReadDir(sourceDir) fails with an error that is not os.IsNotExist while creating the agy config overlay via createAgyConfigOverlay. E.g. the source dir exists but the process lacks read permission, the path is a regular file, or a disk I/O error occurs.","commonSituations":"Running cc-connect as a user that cannot read ~/.antigravity (or similar) config owned by another user; a stale file replaced the config directory; NFS/readonly filesystem failures.","solutions":["Check the directory is readable by the current user: ls -la <dir> and fix ownership/permissions (chmod/chown).","Verify the path is actually a directory, not a file: file <dir>.","If the directory should be optional/empty, ensure it exists with correct permissions or remove the stale file so IsNotExist handling applies.","Inspect the wrapped cause (%w) in the message for the underlying OS error."],"exampleFix":"// before: source is a stale file\n$ rm ~/.antigravity && mkdir ~/.antigravity\n// after\n$ mkdir -p ~/.antigravity && chmod 700 ~/.antigravity","handlingStrategy":"validation","validationCode":"info, err := os.Stat(sourceDir)\nif err != nil || !info.IsDir() {\n    // directory missing or not a directory — fix perms or recreate\n}\nif info != nil && info.IsDir() {\n    if f, err := os.Open(sourceDir); err != nil { /* unreadable */ } else { f.Close() }\n}","typeGuard":null,"tryCatchPattern":"if err := createAgyConfigOverlay(...); err != nil && strings.HasPrefix(err.Error(), \"read Agy config directory\") {\n    slog.Warn(\"agy config dir unreadable, skipping overlay\", \"err\", err)\n}","preventionTips":["Run cc-connect as the same user that owns the antigravity config directory.","Check that the source path is a directory before overlay creation.","Monitor filesystem health on network mounts."],"tags":["go","filesystem","antigravity","config-overlay"],"backgroundTag":"file-read-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"}