plandex-ai/plandex · error

planningSharedMsgs is empty - required for planning stage

Error message

planningSharedMsgs is empty - required for planning stage

What it means

Thrown by getTellSysPrompt (tell_sys_prompt.go:49) when the tell stream is in the Planning stage but the planStageSharedMsgs (planningSharedMsgs) slice is empty and the call is not a dry run (params.dryRunWithoutContext is false). Planning-stage system prompts require the shared plan context messages (project context, plan context, etc.) as their base; without them the prompt would be built with no shared context, so the library refuses to construct it. Callers are execTellPlan and dryRunCalculateTokensWithoutContext.

Source

Thrown at app/server/model/plan/tell_sys_prompt.go:49

	currentStage := state.currentStage

	sysParts := []types.ExtendedChatMessagePart{}

	createPromptParams := prompts.CreatePromptParams{
		ExecMode:          req.ExecEnabled,
		AutoContext:       req.AutoContext,
		IsUserDebug:       req.IsUserDebug,
		IsApplyDebug:      req.IsApplyDebug,
		IsGitRepo:         req.IsGitRepo,
		ContextTokenLimit: contextTokenLimit,
	}

	// log.Println("getTellSysPrompt - prompt params:", spew.Sdump(params))

	if currentStage.TellStage == shared.TellStagePlanning {
		if len(planningSharedMsgs) == 0 && !params.dryRunWithoutContext {
			log.Println("planningSharedMsgs is empty - required for planning stage")
			return nil, fmt.Errorf("planningSharedMsgs is empty - required for planning stage")
		}

		for _, msg := range planningSharedMsgs {
			sysParts = append(sysParts, *msg)
		}

		if currentStage.PlanningPhase == shared.PlanningPhaseContext {
			log.Println("Planning phase is context -- adding auto context prompt")

			var txt string
			if req.IsChatOnly {
				txt = prompts.GetAutoContextChatPrompt(createPromptParams)
			} else {
				txt = prompts.GetAutoContextTellPrompt(createPromptParams)
			}

			sysParts = append(sysParts, types.ExtendedChatMessagePart{
				Type: openai.ChatMessagePartTypeText,

View on GitHub (pinned to e2d772072e)

Solutions

  1. Ensure execTellPlan populates planStageSharedMsgs from the loaded plan/project context before calling getTellSysPrompt for the planning stage.
  2. Confirm context loading (auto-load paths, plan context) returns at least one message; fix the loader if it silently returns empty.
  3. For token-estimation paths, set dryRunWithoutContext=true so the empty-check is bypassed intentionally.
  4. Add an earlier guard/log when planStageSharedMsgs ends up empty so the failure is caught before prompt construction.

Example fix

// before
params := getTellSysPromptParams{planningPhaseOnlyMsgs: plannerOnly}
// after
if len(sharedMsgs) == 0 && !params.dryRunWithoutContext {
    return nil, fmt.Errorf("cannot start planning stage: no shared plan context loaded")
}
params := getTellSysPromptParams{planStageSharedMsgs: sharedMsgs, planningPhaseOnlyMsgs: plannerOnly}
Defensive patterns

Strategy: validation

Validate before calling

func canBuildPlanningPrompt(sharedMsgs []*types.ExtendedChatMessagePart, dryRun bool) error {
    if len(sharedMsgs) == 0 && !dryRun {
        return fmt.Errorf("planning stage requires planStageSharedMsgs; got %d messages", len(sharedMsgs))
    }
    return nil
}

Type guard

func hasPlanningSharedMsgs(p getTellSysPromptParams) bool {
    return p.dryRunWithoutContext || len(p.planStageSharedMsgs) > 0
}

Try / catch

sysParts, err := state.getTellSysPrompt(params)
if err != nil {
    if strings.Contains(err.Error(), "planningSharedMsgs is empty") {
        // rebuild shared context then retry once
        if sharedMsgs, lerr := loadPlanStageSharedMsgs(state); lerr == nil {
            params.planStageSharedMsgs = sharedMsgs
            sysParts, err = state.getTellSysPrompt(params)
        }
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: execTellPlan reaches the planning stage while planStageSharedMsgs was built as an empty slice — e.g. context loading produced zero messages, or a code path that composes getTellSysPromptParams forgets to populate planStageSharedMsgs during a real (non-dry-run) planning request.

Common situations: A regression in prompt-assembly code that skips loading plan/shared context; a plan with no loaded context files combined with a code path that doesn't fall back to a minimal shared message; custom integrations calling the tell pipeline directly without supplying planStageSharedMsgs.

Related errors


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