{"record":{"id":"7b995fd64c7379a8","repo":"plandex-ai/plandex","slug":"error-reading-description-file-s-v","errorCode":null,"errorMessage":"error reading description file %s: %v","messagePattern":"error reading description file (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":340,"sourceCode":"\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\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error reading description file %s: %v\", file.Name(), err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar description ConvoMessageDescription\n\t\t\terr = json.Unmarshal(bytes, &description)\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"Error unmarshalling description file:\", path)\n\t\t\t\tlog.Println(\"bytes:\")\n\t\t\t\tlog.Println(string(bytes))\n\n\t\t\t\terrCh <- fmt.Errorf(\"error unmarshalling description file %s: %v\", path, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdescCh <- &description\n\t\t}(file)\n\t}","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L322-L358","documentation":"Inside each per-file goroutine of GetConvoMessageDescriptions, os.ReadFile loads the description JSON. Any read failure is wrapped as 'error reading description file %s: %v' naming the file and sent to errCh, which the collector turns into 'error reading description files: ...' and aborts the entire descriptions load.","triggerScenarios":"Calling GetConvoMessageDescriptions when a listed description file cannot be read: it was deleted between ReadDir and ReadFile, permissions changed, it is a directory/special file inside descriptionsDir, or the underlying filesystem returns an I/O error.","commonSituations":"Another process (cleanup job, concurrent plan reset) deleting description files while this read runs; permission drift after container image updates or user changes; a subdirectory accidentally created inside descriptionsDir; read-only remount of the data volume.","solutions":["Identify the failing file from the message and check whether it still exists and is readable","Check permissions/ownership of that file and the descriptions directory for the server user","Remove non-regular entries (directories, sockets) from the descriptions dir or filter them with file.Type().IsRegular()","Stop concurrent cleanup processes from racing with reads, or tolerate individual file misses by skipping on os.IsNotExist","Investigate filesystem health if the inner error is an I/O error (EIO), remounting or repairing the volume"],"exampleFix":"// before\nbytes, err := os.ReadFile(path)\nif err != nil {\n    errCh <- fmt.Errorf(\"error reading description file %s: %v\", file.Name(), err)\n    return\n}\n// after\nbytes, err := os.ReadFile(path)\nif err != nil {\n    if os.IsNotExist(err) {\n        descCh <- nil // file vanished between listing and read; skip it\n        return\n    }\n    errCh <- fmt.Errorf(\"error reading description file %s: %v\", file.Name(), err)\n    return\n}","handlingStrategy":"validation","validationCode":"func descriptionFileReadable(dir string, name string) error {\n    path := filepath.Join(dir, name)\n    info, err := os.Stat(path)\n    if err != nil {\n        return err\n    }\n    if !info.Mode().IsRegular() {\n        return fmt.Errorf(\"%s is not a regular file\", path)\n    }\n    f, err := os.Open(path)\n    if err != nil {\n        return err\n    }\n    return f.Close()\n}","typeGuard":"func isDescriptionFileReadError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"error reading description file\")\n}","tryCatchPattern":"descriptions, err := GetConvoMessageDescriptions(orgId, planId)\nif err != nil {\n    if isDescriptionFileReadError(err) {\n        var badFile string\n        fmt.Sscanf(err.Error(), \"error reading description files: error reading description file %s\", &badFile)\n        log.Printf(\"unreadable description file %q; repair or remove it\", badFile)\n        return nil, err\n    }\n    return nil, err\n}","preventionTips":["Don't run cleanup jobs that delete description files while plan state is being read","Keep volume read/write (not read-only remounted) for the plan data root","Filter non-regular entries from descriptions directories","Grant the server user read permission on all description files"],"tags":["go","filesystem","file-read","concurrency"],"backgroundTag":"file-read-failed","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"}