plandex-ai/plandex · error
error getting plan modelContext: %v
Error message
error getting plan modelContext: %v
What it means
This error is produced by the recover() handler in the getPlanContexts goroutine: any panic raised while assembling the plan's model context is converted into this error, sent to errCh, and the goroutine exits via runtime.Goexit so the outer function does not double-send. It means the context-loading goroutine crashed unexpectedly rather than returning a normal error — typically a nil map/slice or index-out-of-range while building modelContext.
Source
Thrown at app/server/model/plan/tell_load.go:145
return nil
})
if err != nil {
log.Printf("Error renaming plan: %v\n", err)
errCh <- fmt.Errorf("error renaming plan: %v", err)
return
}
}
errCh <- nil
}()
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in getPlanContexts: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("error getting plan modelContext: %v", r)
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
if iteration > 0 || missingFileResponse != "" {
modelContext = active.Contexts
} else {
res, err := db.GetPlanContexts(currentOrgId, planId, true, false)
if err != nil {
log.Printf("Error getting plan modelContext: %v\n", err)
errCh <- fmt.Errorf("error getting plan modelContext: %v", err)
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)
// }View on GitHub (pinned to e2d772072e)
Solutions
- Read the 'panic in getPlanContexts' log line with its stack trace to locate the panicking code
- Add nil/range checks around active.Contexts and the GetPlanContexts result before use
- Fix the underlying nil-pointer or index bug identified by the stack trace
- Re-run the request — this is a software defect, not a transient state
- Consider making the goroutine return errors instead of panicking so failures are typed and testable
Example fix
// before
if iteration > 0 || missingFileResponse != "" {
modelContext = active.Contexts
}
// after
if iteration > 0 || missingFileResponse != "" {
if active == nil || active.Contexts == nil {
errCh <- fmt.Errorf("no active plan contexts available")
return
}
modelContext = active.Contexts
} Defensive patterns
Strategy: try-catch
Validate before calling
// guard before the goroutine body runs
if active == nil || active.Ctx == nil {
return fmt.Errorf("no active plan available for context loading")
}
if iteration > 0 || missingFileResponse != "" {
if active.Contexts == nil {
return fmt.Errorf("active plan has no cached contexts")
}
} Type guard
func hasActiveContexts(ap *types.ActivePlan) bool {
return ap != nil && ap.Contexts != nil && len(ap.Contexts) > 0
} Try / catch
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in getPlanContexts: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("error getting plan modelContext: %v", r)
runtime.Goexit()
}
}()
... // goroutine body
}()
// consumer side:
if err := <-errCh; err != nil {
if strings.HasPrefix(err.Error(), "error getting plan modelContext") {
// treat as unrecoverable software defect: fail request, alert with stack trace
}
} Prevention
- Nil-check active plan fields (Contexts, Ctx) before dereferencing in goroutines
- Avoid unsynchronized reads/writes of ActivePlan shared across goroutines
- Prefer returning errors over panics so failures are typed and testable
- Always pair recover() with debug.Stack() logging as done here, and use runtime.Goexit to prevent double channel sends
- Add unit tests covering iteration==0 with empty/missing context data
When it happens
Trigger: A panic in the getPlanContexts goroutine — e.g. dereferencing a nil active plan/context, indexing into an empty modelContext slice, or a nil-pointer in code operating on active.Contexts or db results before the deferred recover fires.
Common situations: Concurrent map/slice access on ActivePlan while other goroutines mutate it; a code change introduced a nil dereference on a freshly created plan (iteration == 0 and no missingFileResponse); library upgrade changing GetPlanContexts return shape handled without nil checks.
Related errors
- panic in UpdateContexts: %v\n%s
- panic in GetPlanConvo: %v\n%s
- panic in GetPlanDiffs: %v\n%s
- panic in gitRemoveIndexLockFileIfExists: %v %s
- panic in DeleteDraftPlans: %v %s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/74584b681f3d7408.
Report an issue: GitHub.