{"record":{"id":"64f4cf4f0557774c","repo":"plandex-ai/plandex","slug":"error-getting-plan-convo-v","errorCode":null,"errorMessage":"error getting plan convo: %v","messagePattern":"error getting plan convo: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/model/plan/build_load.go","lineNumber":273,"sourceCode":"\t\t\t\tactivePlan.StreamDoneCh <- &shared.ApiError{\n\t\t\t\t\tType:   shared.ApiErrorTypeOther,\n\t\t\t\t\tStatus: http.StatusInternalServerError,\n\t\t\t\t\tMsg:    \"Error getting current plan state: \" + err.Error(),\n\t\t\t\t}\n\t\t\t\terrCh <- fmt.Errorf(\"error getting current plan state: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcurrentPlan = res\n\n\t\t\tlog.Println(\"Got current plan state\")\n\t\t\terrCh <- nil\n\t\t}()\n\n\t\tgo func() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in getPlanConvo: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"error getting plan convo: %v\", r)\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\tres, err := db.GetPlanConvo(currentOrgId, planId)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Error getting plan convo: %v\\n\", err)\n\t\t\t\terrCh <- fmt.Errorf(\"error getting plan convo: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconvo = res\n\n\t\t\terrCh <- nil\n\t\t}()\n\n\t\tfor i := 0; i < 2; i++ {\n\t\t\terr = <-errCh\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Error getting plan data: %v\\n\", err)","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/build_load.go#L255-L291","documentation":"The second goroutine in loadBuildFile, which fetches the plan conversation via db.GetPlanConvo, is guarded by a recover() defer. If that goroutine panics, the panic value is logged with a stack trace, wrapped as \"error getting plan convo: %v\", and sent to errCh before runtime.Goexit() prevents a duplicate send. This error signals a crash (not a normal db error) while loading the plan's conversation messages.","triggerScenarios":"A panic happens inside the goroutine calling db.GetPlanConvo(currentOrgId, planId) — typically a nil pointer dereference or out-of-range access while assembling the conversation message list.","commonSituations":"Convo messages contain unexpected shapes (nil fields, malformed JSON deserialized into slices) causing db layer panics; concurrent writes to the convo table during read; a regression in db.GetPlanConvo after a code change.","solutions":["Read the panic stack trace logged immediately before this error to locate the crashing line in db.GetPlanConvo","Inspect the plan's convo messages for nil/malformed entries that could break the db layer","Add nil/bounds guards in db.GetPlanConvo around message assembly","Rebuild; the outer handler resets IsBuildingByPath and reports a 500 via StreamDoneCh"],"exampleFix":"// before\nres, err := db.GetPlanConvo(currentOrgId, planId) // panics on nil message field\n// after\nfor _, m := range rawMessages {\n\tif m == nil {\n\t\tcontinue // skip malformed rows instead of dereferencing\n\t}\n}","handlingStrategy":"try-catch","validationCode":"if currentOrgId == \"\" || planId == \"\" {\n\treturn fmt.Errorf(\"cannot load plan convo: orgId and planId are required\")\n}","typeGuard":"func hasConvoParams(orgId, planId string) bool {\n\treturn orgId != \"\" && planId != \"\"\n}","tryCatchPattern":"res, err := safeGetPlanConvo(currentOrgId, planId) // recovers panics internally\nif err != nil {\n\tlog.Printf(\"plan convo unavailable, aborting load: %v\", err)\n\treturn err\n}","preventionTips":["Guard db.GetPlanConvo against nil fields in convo message rows","Fuzz/deserialize convo rows defensively (skip malformed messages)","Watch logs for \"panic in getPlanConvo\" and fix the underlying db-layer bug","Test convo loading with empty and partial conversation histories"],"tags":["go","panic","concurrency","database"],"backgroundTag":"goroutine-panic-recovered","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"}