{"record":{"id":"0b312b7ee6550c5f","repo":"plandex-ai/plandex","slug":"error-reading-applies-dir-v","errorCode":null,"errorMessage":"error reading applies dir: %v","messagePattern":"error reading applies dir: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":1038,"sourceCode":"\t}\n\n\tif !foundReplacement {\n\t\treturn fmt.Errorf(\"replacement not found: %s\", replacementId)\n\t}\n\n\treturn nil\n}\n\nfunc GetPlanApplies(orgId, planId string) ([]*PlanApply, error) {\n\tappliesDir := getPlanAppliesDir(orgId, planId)\n\tfiles, err := os.ReadDir(appliesDir)\n\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"error reading applies dir: %v\", err)\n\t}\n\n\tplanApplies := []*PlanApply{}\n\tvar mu sync.Mutex\n\n\terrCh := make(chan error, len(files))\n\n\tfor _, file := range files {\n\t\tgo func(file os.DirEntry) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in GetPlanApplies: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in GetPlanApplies: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t\t}\n\t\t\t}()\n\t\t\tbytes, err := os.ReadFile(filepath.Join(appliesDir, file.Name()))\n","sourceCodeStart":1020,"sourceCodeEnd":1056,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L1020-L1056","documentation":"GetPlanApplies reads the plan's applies directory with os.ReadDir. Missing directories (os.IsNotExist) are treated as \"no applies\" and return nil, nil; any OTHER read error is wrapped as this error. It means the directory exists (or otherwise failed) but could not be listed — typically permissions or I/O problems.","triggerScenarios":"Calling GetPlanApplies when the applies directory exists but ReadDir fails for a non-not-exist reason: permission denied, path is a file not a directory, or an I/O/disk error.","commonSituations":"Manual intervention replaced the applies directory with a regular file; chmod/chown changes on the data dir; running the server under a different user than the one that created the data.","solutions":["Check permissions on <planDir>/applies and its parents (server process user needs read+execute).","Verify the path is a directory, not a file: 'ls -la' / 'stat' on the applies path.","Check disk health / dmesg for I/O errors if failures are intermittent.","Fix ownership to match the server process user."],"exampleFix":"// before\napplies, err := db.GetPlanApplies(orgId, planId)\n// after (pre-check on host)\n// stat <data>/orgs/<orgId>/plans/<planId>/applies && ensure it is a dir with correct perms\napplies, err := db.GetPlanApplies(orgId, planId)","handlingStrategy":"validation","validationCode":"appliesPath := filepath.Join(getPlanDir(orgId, planId), \"applies\")\nif info, err := os.Stat(appliesPath); err == nil && !info.IsDir() {\n    return fmt.Errorf(\"%s is not a directory\", appliesPath)\n}\napplies, err := GetPlanApplies(orgId, planId)","typeGuard":null,"tryCatchPattern":"applies, err := GetPlanApplies(orgId, planId)\nif err != nil {\n    if strings.Contains(err.Error(), \"error reading applies dir\") {\n        return nil, fmt.Errorf(\"cannot list applies dir (check permissions/path): %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Ensure the server process user owns or has read+execute on the plan data directories.","Run filesystem health checks if errors are intermittent.","Don't replace data directories with regular files during maintenance."],"tags":["go","filesystem","permissions"],"backgroundTag":"permission-denied","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}