{"record":{"id":"76fee0dddef50e23","repo":"plandex-ai/plandex","slug":"error-reading-descriptions-dir-v","errorCode":null,"errorMessage":"error reading descriptions dir: %v","messagePattern":"error reading descriptions dir: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":320,"sourceCode":"\t}\n\n\tplanState.CurrentPlanFiles = currentPlanFiles\n\n\treturn planState, nil\n}\n\nfunc GetConvoMessageDescriptions(orgId, planId string) ([]*ConvoMessageDescription, error) {\n\tvar descriptions []*ConvoMessageDescription\n\tdescriptionsDir := getPlanDescriptionsDir(orgId, planId)\n\tfiles, err := os.ReadDir(descriptionsDir)\n\n\tif err != nil {\n\n\t\tif os.IsNotExist(err) {\n\t\t\treturn descriptions, nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"error reading descriptions dir: %v\", err)\n\t}\n\n\terrCh := make(chan error, len(files))\n\tdescCh := make(chan *ConvoMessageDescription, 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 GetConvoMessageDescriptions: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in GetConvoMessageDescriptions: %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\tpath := filepath.Join(descriptionsDir, file.Name())\n\n\t\t\tbytes, err := os.ReadFile(path)\n","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L302-L338","documentation":"GetConvoMessageDescriptions reads a plan's descriptions directory with os.ReadDir. A missing directory is treated as empty (os.IsNotExist short-circuit), but any other ReadDir failure (permissions, not a directory, I/O error) is wrapped as 'error reading descriptions dir: %v' and returned, failing the whole descriptions load.","triggerScenarios":"Calling GetConvoMessageDescriptions (directly or via GetCurrentPlanState / ClearContext / PendingBuildsByPath / invalidateConflictedResults) when descriptionsDir exists but cannot be listed: wrong permissions, path is a file not a directory, or an I/O error during listing.","commonSituations":"Server run under a different user than the one that created plan data (permission denied); getPlanDescriptionsDir path misconfigured after a data-root move so it points at a regular file; NFS/overlay filesystem returning EIO; symlink loops.","solutions":["Read the wrapped inner error (errno) to identify permission vs not-a-directory vs I/O","Check permissions/ownership of the descriptions directory for the server process user","Confirm getPlanDescriptionsDir(orgId, planId) resolves to the correct data-root path","If the path is a file or symlink target is broken, fix or remove it and recreate the directory","If the plan has no descriptions and the dir is genuinely absent, no fix is needed — the library returns an empty list"],"exampleFix":"// before\nfiles, err := os.ReadDir(descriptionsDir)\nif err != nil {\n    return nil, fmt.Errorf(\"error reading descriptions dir: %v\", err)\n}\n// after\nfiles, err := os.ReadDir(descriptionsDir)\nif err != nil {\n    if os.IsNotExist(err) || os.IsPermission(err) {\n        return descriptions, nil // treat unreadable/missing dir as no descriptions\n    }\n    return nil, fmt.Errorf(\"error reading descriptions dir: %v\", err)\n}","handlingStrategy":"validation","validationCode":"func descriptionsDirReadable(orgId, planId string) error {\n    dir := getPlanDescriptionsDir(orgId, planId)\n    info, err := os.Stat(dir)\n    if os.IsNotExist(err) {\n        return nil // treated as empty by the library\n    }\n    if err != nil {\n        return err\n    }\n    if !info.IsDir() {\n        return fmt.Errorf(\"%s is not a directory\", dir)\n    }\n    f, err := os.Open(dir)\n    if err != nil {\n        return err\n    }\n    return f.Close()\n}","typeGuard":"func isDescriptionsDirError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"error reading descriptions dir\")\n}","tryCatchPattern":"descriptions, err := GetConvoMessageDescriptions(orgId, planId)\nif err != nil {\n    if isDescriptionsDirError(err) {\n        log.Printf(\"descriptions dir unreadable, treating as empty: %v\", err)\n        descriptions = nil\n    } else {\n        return nil, err\n    }\n}","preventionTips":["Check the data-root path configuration after any deployment or volume move","Ensure the directory is a directory, not a file or symlink to a file","Keep server process user ownership consistent on the plan data root","Remember: a missing dir is fine (returns empty); only other errors are fatal"],"tags":["go","filesystem","permissions","directory-read"],"backgroundTag":"directory-read-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}