{"record":{"id":"a77493607ccb4570","repo":"plandex-ai/plandex","slug":"error-getting-plan-summaries-v","errorCode":null,"errorMessage":"error getting plan summaries: %v","messagePattern":"error getting plan summaries: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/model/plan/tell_load.go","lineNumber":246,"sourceCode":"\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Printf(\"[TellLoad] Error storing user message: %v\\n\", err)\n\t\t\t\t\t\tinnerErrCh <- fmt.Errorf(\"error storing user message: %v\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tUpdateActivePlan(planId, branch, func(ap *types.ActivePlan) {\n\t\t\t\t\t\tap.MessageNum = num\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tinnerErrCh <- nil\n\t\t\t}()\n\n\t\t\tgo func() {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tlog.Printf(\"panic in getPlanSummaries: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\t\tinnerErrCh <- fmt.Errorf(\"error getting plan summaries: %v\", r)\n\t\t\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t\t\t}\n\t\t\t\t}()\n\n\t\t\t\tvar convoMessageIds []string\n\n\t\t\t\tfor _, convoMessage := range convo {\n\t\t\t\t\tconvoMessageIds = append(convoMessageIds, convoMessage.Id)\n\t\t\t\t}\n\n\t\t\t\tlog.Println(\"getting plan summaries\")\n\t\t\t\tlog.Println(\"convoMessageIds:\", convoMessageIds)\n\n\t\t\t\tres, err := db.GetPlanSummaries(planId, convoMessageIds)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"Error getting plan summaries: %v\\n\", err)\n\t\t\t\t\tinnerErrCh <- fmt.Errorf(\"error getting plan summaries: %v\", err)\n\t\t\t\t\treturn","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/tell_load.go#L228-L264","documentation":"This error is produced by the getPlanSummaries goroutine's recover handler: the goroutine that loads the plan's conversation summaries panicked. The recovered value is wrapped and sent on innerErrCh so loadTellPlan aborts cleanly rather than crashing the server.","triggerScenarios":"A panic inside the goroutine guarded at tell_load.go:246 — e.g., nil dereference while building convoMessageIds from convo entries with nil pointers, or a panic inside db.GetPlanSummaries(planId, convoMessageIds) or while reading the last summary's .Summary field.","commonSituations":"Corrupted convo entries with missing Ids or nil fields in the DB; convo slice mutated concurrently by another goroutine; nil summaries entry accessed via summaries[len-1].Summary after a partial query result.","solutions":["Inspect the 'panic in getPlanSummaries' stack trace to find the exact deref site","Skip convo messages with empty/nil Ids when building convoMessageIds","Validate GetPlanSummaries results before indexing summaries[len-1]","Run with -race to check for concurrent mutation of the shared convo slice"],"exampleFix":"// before\nfor _, convoMessage := range convo {\n    convoMessageIds = append(convoMessageIds, convoMessage.Id)\n}\n// after\nfor _, convoMessage := range convo {\n    if convoMessage.Id != \"\" {\n        convoMessageIds = append(convoMessageIds, convoMessage.Id)\n    }\n}","handlingStrategy":"type-guard","validationCode":"if len(convo) == 0 {\n    log.Println(\"no convo messages; skipping summaries fetch\")\n    return nil\n}\nfor _, m := range convo {\n    if m.Id == \"\" {\n        return fmt.Errorf(\"convo message missing Id; cannot fetch summaries safely\")\n    }\n}","typeGuard":"func hasValidConvoIds(convo []db.ConvoMessage) bool {\n    for _, m := range convo {\n        if m.Id == \"\" {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"if err := <-innerErrCh; err != nil && strings.HasPrefix(err.Error(), \"error getting plan summaries:\") {\n    // panic-recovery path — stack already logged; proceed with empty summaries or abort\n    return err\n}","preventionTips":["Filter out convo entries with empty Ids before building convoMessageIds","Never mutate the shared convo slice from other goroutines while summaries load","Bounds-check summaries before accessing the last element","Keep the defer/recover wrapper on every goroutine sending to innerErrCh"],"tags":["panic","goroutine","recovered-panic","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"}