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

  1. Use the stack trace in the message (after the newline) to pinpoint the panicking line and fix the nil/invalid state access.
  2. Validate settings/model pack configuration — a missing coder model in the model pack is a common nil source.
  3. Reproduce with -race to detect concurrent mutation of active plan state.
  4. 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

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


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/7f53d0707b99156d. Report an issue: GitHub.