{"record":{"id":"3d6bb8c9ab8de695","repo":"plandex-ai/plandex","slug":"error-reading-apply-file-v","errorCode":null,"errorMessage":"error reading apply file: %v","messagePattern":"error reading apply file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":1058,"sourceCode":"\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\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error reading apply file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar apply PlanApply\n\t\t\terr = json.Unmarshal(bytes, &apply)\n\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error unmarshalling apply file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmu.Lock()\n\t\t\tplanApplies = append(planApplies, &apply)\n\t\t\tmu.Unlock()\n\n\t\t\terrCh <- nil\n\t\t}(file)\n\t}","sourceCodeStart":1040,"sourceCodeEnd":1076,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L1040-L1076","documentation":"Inside GetPlanApplies' per-file goroutine, os.ReadFile of an individual apply file failed; the error is sent on errCh and later wrapped as \"error getting plan applies\". A file can disappear between ReadDir and ReadFile, or be unreadable due to permissions or I/O errors.","triggerScenarios":"An apply JSON file in the applies directory was deleted/renamed concurrently (race between listing and reading), is a directory itself, or is unreadable by the server process.","commonSituations":"Concurrent cleanup or plan deletion while another request lists applies; tmp/partial files written by another process; permission drift on the data dir.","solutions":["Re-run the call; transient ENOENT races usually resolve once the concurrent deletion finishes.","Check permissions on the apply files and the applies directory.","Identify which file fails from the wrapped error path and remove/repair it if corrupt.","Avoid external processes touching the plan data directory while the server runs."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var applies []*db.PlanApply\nvar err error\nfor i := 0; i < 3; i++ {\n    applies, err = GetPlanApplies(orgId, planId)\n    if err == nil || !strings.Contains(err.Error(), \"error reading apply file\") {\n        break\n    }\n    time.Sleep(100 * time.Millisecond) // transient ENOENT race from concurrent deletion\n}","preventionTips":["Don't run external cleanup/deletion scripts against the applies dir while the server is serving reads.","Make deletes of apply files atomic (rename to trash dir first) to shrink the race window.","Check permissions on apply files after any data migration."],"tags":["go","filesystem","race-condition"],"backgroundTag":"file-not-found","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"}