plandex-ai/plandex · critical
Panic in execTellPlan: %v %s
Error message
Panic in execTellPlan: %v %s
What it means
execTellPlan installs a deferred recover() so any panic during the tell-stream execution loop is converted into an ApiError pushed to StreamDoneCh with message "Panic in execTellPlan: %v\n%s" (panic value plus stack trace), and is also reported to error tracking. It signals an unexpected programming fault (nil map access, index panic, type assertion failure) inside the plan's tell execution rather than a handled error path.
Source
Thrown at app/server/model/plan/tell_exec.go:114
unfinishedSubtaskReasoning := params.unfinishedSubtaskReasoning
log.Printf("[TellExec] Starting iteration %d for plan %s on branch %s", iteration, plan.Id, branch)
currentUserId := auth.User.Id
currentOrgId := auth.OrgId
active := GetActivePlan(plan.Id, branch)
if active == nil {
log.Printf("execTellPlan: Active plan not found for plan ID %s on branch %s\n", plan.Id, branch)
return
}
defer func() {
if r := recover(); r != nil {
log.Printf("execTellPlan: Panic: %v\n%s\n", r, string(debug.Stack()))
go notify.NotifyErr(notify.SeverityError, fmt.Errorf("execTellPlan: Panic: %v\n%s", r, string(debug.Stack())))
active.StreamDoneCh <- &shared.ApiError{
Type: shared.ApiErrorTypeOther,
Status: http.StatusInternalServerError,
Msg: fmt.Sprintf("Panic in execTellPlan: %v\n%s", r, string(debug.Stack())),
}
}
}()
if missingFileResponse == "" {
log.Println("Executing WillExecPlanHook")
_, apiErr := hooks.ExecHook(hooks.WillExecPlan, hooks.HookParams{
Auth: auth,
Plan: plan,
})
if apiErr != nil {
time.Sleep(100 * time.Millisecond)View on GitHub (pinned to e2d772072e)
Solutions
- Use the stack trace in the message (after the newline) to pinpoint the panicking line and fix the nil/invalid state access.
- Validate settings/model pack configuration — a missing coder model in the model pack is a common nil source.
- Reproduce with -race to detect concurrent mutation of active plan state.
- Escalate to Plandex maintainers with the full stack trace if the panic originates in framework code.
Defensive patterns
Strategy: fallback
Validate before calling
// validate model pack settings before Tell so nil coder cannot panic the executor
if state.settings.GetModelPack().GetCoder() == nil {
return errors.New("model pack has no coder model configured")
} Try / catch
apiErr := <-active.StreamDoneCh
if strings.HasPrefix(apiErr.Msg, "Panic in execTellPlan") {
stack := apiErr.Msg[strings.Index(apiErr.Msg, "\n")+1:]
log.Printf("tell plan panicked:\n%s", stack)
// fall back: restart the tell from scratch rather than resuming corrupted stream state
return restartTell(planId, branch)
} Prevention
- Always configure a coder model in every model pack you use.
- Run with -race in CI/staging to surface concurrent state mutation early.
- Pin matching versions of plandex-server and plandex-shared.
- Report recurring panics to maintainers with the embedded stack trace.
When it happens
Trigger: Any unrecovered panic inside execTellPlan — typically nil pointer on state/settings/model config, index-out-of-range while slicing convo messages, or a failed type assertion on model responses — triggered via Tell, handleStreamFinished, or handleMissingFile.
Common situations: Model packs / settings returning nil coder after a config change; concurrent writes to the active plan map from multiple goroutines; a bug in prompt-building code hitting an empty slice after an upgrade.
Related errors
- panic in UpdateContexts: %v\n%s
- panic in GetPlanConvo: %v\n%s
- panic in gitRemoveIndexLockFileIfExists: %v %s
- panic in DeleteDraftPlans: %v %s
- panic in GetFullCurrentPlanStateParams: %v\n%s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/7f53d0707b99156d.
Report an issue: GitHub.