{"record":{"id":"14fbcfa0d560e5ca","repo":"plandex-ai/plandex","slug":"error-getting-latest-plan-build-description-v","errorCode":null,"errorMessage":"error getting latest plan build description: %v","messagePattern":"error getting latest plan build description: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":99,"sourceCode":"\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"error getting plan file results: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tresults = res\n\t\terrCh <- nil\n\t}()\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tlog.Printf(\"panic in GetFullCurrentPlanStateParams: %v\\n%s\", r, debug.Stack())\n\t\t\t\terrCh <- fmt.Errorf(\"panic in GetFullCurrentPlanStateParams: %v\\n%s\", r, debug.Stack())\n\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t}\n\t\t}()\n\t\tres, err := GetConvoMessageDescriptions(orgId, planId)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"error getting latest plan build description: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tconvoMessageDescriptions = res\n\t\terrCh <- nil\n\t}()\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tlog.Printf(\"panic in GetFullCurrentPlanStateParams: %v\\n%s\", r, debug.Stack())\n\t\t\t\terrCh <- fmt.Errorf(\"panic in GetFullCurrentPlanStateParams: %v\\n%s\", r, debug.Stack())\n\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t}\n\t\t}()\n\t\tres, err := GetPlanContexts(orgId, planId, true, false)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"error getting contexts: %v\", err)\n\t\t\treturn","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L81-L117","documentation":"When GetConvoMessageDescriptions fails inside GetFullCurrentPlanStateParams (despite its name, this is the descriptions loader), the error is re-wrapped with this message and returned to the caller as the plan state load failure.","triggerScenarios":"GetConvoMessageDescriptions returns an error: descriptions dir unreadable (non-ENOENT), a description file unreadable, or any description file fails JSON unmarshal — each is bubbled up wrapped by this message.","commonSituations":"A truncated description file from a prior crash; hand-edited JSON; permissions issue on the descriptions dir; concurrent writer deleting files mid-read.","solutions":["Inspect the wrapped child error to find the offending file or directory","Fix file permissions on the descriptions directory","Delete or repair corrupt description JSON files","Stop concurrent cleanup processes racing with reads","Add tolerant parsing if a single bad description should not block plan state loading"],"exampleFix":"// before\nif err != nil {\n\treturn CurrentPlanStateParams{}, err\n}\n// after\nif err != nil {\n\tlog.Printf(\"descriptions load failed for %s/%s: %v\", orgId, planId, err)\n\treturn CurrentPlanStateParams{OrgId: orgId, PlanId: planId}, nil\n}","handlingStrategy":"fallback","validationCode":"func descriptionsHealthy(orgId, planId string) bool {\n\tdir := getPlanDescriptionsDir(orgId, planId)\n\tfiles, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn os.IsNotExist(err)\n\t}\n\tfor _, f := range files {\n\t\tb, err := os.ReadFile(filepath.Join(dir, f.Name()))\n\t\tif err != nil || !json.Valid(b) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":"func isCorruptJSON(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"unmarshal\")\n}","tryCatchPattern":"params, err := db.GetFullCurrentPlanStateParams(orgId, planId)\nif err != nil && strings.Contains(err.Error(), \"error getting latest plan build description\") {\n\tlog.Printf(\"falling back to state without descriptions: %v\", err)\n\tparams.ConvoMessageDescriptions = nil\n}","preventionTips":["Use atomic writes (temp+rename) when storing descriptions so crashes never leave truncated files","Make single corrupt description files skip-and-log rather than fail-all","Lock down data dir permissions to the service account only","Stop cleanup scripts from racing live reads","Verify JSON validity after any restore/migration"],"tags":["go","json","filesystem","io"],"backgroundTag":"json-unmarshal-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"}