plandex-ai/plandex · error
error getting plan convo: %v
Error message
error getting plan convo: %v
What it means
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.
Source
Thrown at app/server/model/plan/tell_load.go:175
return
}
log.Printf("[TellLoad] Tell plan - loadTellPlan - modelContext: %v\n", len(modelContext))
// for _, part := range modelContext {
// log.Printf("[TellLoad] Tell plan - loadTellPlan - part: %s - %s - %s - %d tokens\n", part.ContextType, part.Name, part.FilePath, part.NumTokens)
// }
modelContext = res
}
errCh <- nil
}()
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in getPlanConvo: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("error getting plan convo: %v", r)
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
res, err := db.GetPlanConvo(currentOrgId, planId)
if err != nil {
log.Printf("Error getting plan convo: %v\n", err)
errCh <- fmt.Errorf("error getting plan convo: %v", err)
return
}
convo = res
UpdateActivePlan(planId, branch, func(ap *types.ActivePlan) {
ap.MessageNum = len(convo)
})
promptTokens := shared.GetNumTokensEstimate(req.Prompt)
innerErrCh := make(chan error, 2)
View on GitHub (pinned to e2d772072e)
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
Example fix
// before
res, err := db.GetPlanConvo(currentOrgId, planId)
// after
if currentOrgId == "" || planId == "" {
errCh <- fmt.Errorf("error getting plan convo: empty orgId or planId")
return
}
res, err := db.GetPlanConvo(currentOrgId, planId) Defensive patterns
Strategy: try-catch
Validate before calling
if currentOrgId == "" || planId == "" {
return fmt.Errorf("cannot load plan convo: empty orgId or planId")
}
if err := db.Ping(); err != nil {
return fmt.Errorf("database unavailable: %v", err)
} Type guard
func isRecoveredPanicError(err error) bool {
var pe *fmt.PathError
_ = pe
return err != nil && strings.HasPrefix(err.Error(), "error getting plan convo: runtime error:")
} Try / catch
// Errors arrive on errCh; handle like a caught panic:
for i := 0; i < numGoroutines; i++ {
if err := <-errCh; err != nil {
log.Printf("plan load aborted: %v", err)
return err
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error storing user message: %v
- error getting plan summaries: %v
- error getting org user config: %v
- error getting plan subtasks: %v %s
- panic in UpdateContexts: %v\n%s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/4f71c11270653958.
Report an issue: GitHub.