{"record":{"id":"4f71c11270653958","repo":"plandex-ai/plandex","slug":"error-getting-plan-convo-v-4f71c1","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/tell_load.go","lineNumber":175,"sourceCode":"\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlog.Printf(\"[TellLoad] Tell plan - loadTellPlan - modelContext: %v\\n\", len(modelContext))\n\t\t\t\t// for _, part := range modelContext {\n\t\t\t\t// \tlog.Printf(\"[TellLoad] Tell plan - loadTellPlan - part: %s - %s - %s - %d tokens\\n\", part.ContextType, part.Name, part.FilePath, part.NumTokens)\n\t\t\t\t// }\n\n\t\t\t\tmodelContext = res\n\t\t\t}\n\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\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\t\t\tUpdateActivePlan(planId, branch, func(ap *types.ActivePlan) {\n\t\t\t\tap.MessageNum = len(convo)\n\t\t\t})\n\n\t\t\tpromptTokens := shared.GetNumTokensEstimate(req.Prompt)\n\t\t\tinnerErrCh := make(chan error, 2)\n","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/tell_load.go#L157-L193","documentation":"This error is produced in the getPlanConvo goroutine of loadTellPlan when the goroutine panics (recovered via defer/recover). It wraps the panic value r into a formatted error and sends it on errCh so the parent function can fail fast instead of crashing. It signals that loading the plan's conversation history from the database panicked unexpectedly.","triggerScenarios":"A panic occurs inside the getPlanConvo goroutine — typically a nil map/pointer dereference, nil db handle, or index-out-of-range while calling db.GetPlanConvo(currentOrgId, planId) or while processing its result in app/server/model/plan/tell_load.go:175.","commonSituations":"Nil or mis-initialized database client/connection pool, corrupted plan state where convo rows have unexpected shape, races on shared state (convo, active plan), or nil currentOrgId/planId passed from an upstream context that was torn down.","solutions":["Check the log line 'panic in getPlanConvo' with its debug.Stack() to find the exact panic site and fix the nil/invalid value there","Verify the db client is initialized and connected before calling loadTellPlan","Validate currentOrgId and planId are non-empty and correspond to an existing plan before starting the goroutines","Look for data races on convo/active plan state under 'go test -race' and add synchronization if needed"],"exampleFix":"// before\nres, err := db.GetPlanConvo(currentOrgId, planId)\n// after\nif currentOrgId == \"\" || planId == \"\" {\n    errCh <- fmt.Errorf(\"error getting plan convo: empty orgId or planId\")\n    return\n}\nres, err := db.GetPlanConvo(currentOrgId, planId)","handlingStrategy":"try-catch","validationCode":"if currentOrgId == \"\" || planId == \"\" {\n    return fmt.Errorf(\"cannot load plan convo: empty orgId or planId\")\n}\nif err := db.Ping(); err != nil {\n    return fmt.Errorf(\"database unavailable: %v\", err)\n}","typeGuard":"func isRecoveredPanicError(err error) bool {\n    var pe *fmt.PathError\n    _ = pe\n    return err != nil && strings.HasPrefix(err.Error(), \"error getting plan convo: runtime error:\")\n}","tryCatchPattern":"// Errors arrive on errCh; handle like a caught panic:\nfor i := 0; i < numGoroutines; i++ {\n    if err := <-errCh; err != nil {\n        log.Printf(\"plan load aborted: %v\", err)\n        return err\n    }\n}","preventionTips":["Always validate orgId/planId before starting loadTellPlan goroutines","Keep the existing defer/recover + errCh pattern in every goroutine; never send twice on a channel after panic","Initialize and health-check the DB client at startup","Run tests with -race to catch shared-state panics"],"tags":["panic","goroutine","database","recovered-panic"],"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-14T00:17:10.932Z"}