plandex-ai/plandex · error

error getting latest plan build description: %v

Error message

error getting latest plan build description: %v

What it means

When GetConvoMessageDescriptions fails inside GetFullCurrentPlanStateParams (despite its name, this is the descriptions loader), the error is re-wrapped with this message and returned to the caller as the plan state load failure.

Source

Thrown at app/server/db/result_helpers.go:99

		if err != nil {
			errCh <- fmt.Errorf("error getting plan file results: %v", err)
			return
		}
		results = res
		errCh <- nil
	}()

	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("panic in GetFullCurrentPlanStateParams: %v\n%s", r, debug.Stack())
				errCh <- fmt.Errorf("panic in GetFullCurrentPlanStateParams: %v\n%s", r, debug.Stack())
				runtime.Goexit() // don't allow outer function to continue and double-send to channel
			}
		}()
		res, err := GetConvoMessageDescriptions(orgId, planId)
		if err != nil {
			errCh <- fmt.Errorf("error getting latest plan build description: %v", err)
			return
		}
		convoMessageDescriptions = res
		errCh <- nil
	}()

	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("panic in GetFullCurrentPlanStateParams: %v\n%s", r, debug.Stack())
				errCh <- fmt.Errorf("panic in GetFullCurrentPlanStateParams: %v\n%s", r, debug.Stack())
				runtime.Goexit() // don't allow outer function to continue and double-send to channel
			}
		}()
		res, err := GetPlanContexts(orgId, planId, true, false)
		if err != nil {
			errCh <- fmt.Errorf("error getting contexts: %v", err)
			return

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the wrapped child error to find the offending file or directory
  2. Fix file permissions on the descriptions directory
  3. Delete or repair corrupt description JSON files
  4. Stop concurrent cleanup processes racing with reads
  5. Add tolerant parsing if a single bad description should not block plan state loading

Example fix

// before
if err != nil {
	return CurrentPlanStateParams{}, err
}
// after
if err != nil {
	log.Printf("descriptions load failed for %s/%s: %v", orgId, planId, err)
	return CurrentPlanStateParams{OrgId: orgId, PlanId: planId}, nil
}
Defensive patterns

Strategy: fallback

Validate before calling

func descriptionsHealthy(orgId, planId string) bool {
	dir := getPlanDescriptionsDir(orgId, planId)
	files, err := os.ReadDir(dir)
	if err != nil {
		return os.IsNotExist(err)
	}
	for _, f := range files {
		b, err := os.ReadFile(filepath.Join(dir, f.Name()))
		if err != nil || !json.Valid(b) {
			return false
		}
	}
	return true
}

Type guard

func isCorruptJSON(err error) bool {
	return err != nil && strings.Contains(err.Error(), "unmarshal")
}

Try / catch

params, err := db.GetFullCurrentPlanStateParams(orgId, planId)
if err != nil && strings.Contains(err.Error(), "error getting latest plan build description") {
	log.Printf("falling back to state without descriptions: %v", err)
	params.ConvoMessageDescriptions = nil
}

Prevention

When it happens

Trigger: GetConvoMessageDescriptions returns an error: descriptions dir unreadable (non-ENOENT), a description file unreadable, or any description file fails JSON unmarshal — each is bubbled up wrapped by this message.

Common situations: A truncated description file from a prior crash; hand-edited JSON; permissions issue on the descriptions dir; concurrent writer deleting files mid-read.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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