plandex-ai/plandex · error

error marshalling plan apply: %v

Error message

error marshalling plan apply: %v

What it means

After all apply goroutines succeed, ApplyPlan builds a PlanApply record and serializes it with json.MarshalIndent before writing it to the plan's applies directory. If marshalling fails, "error marshalling plan apply: %v" is returned. In practice this is rare — PlanApply is a plain struct of strings, UUIDs, and timestamps — and almost always indicates an impossible-to-marshal field was added to the struct (e.g., a channel, func, or unsupported custom type) rather than bad caller input.

Source

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

	var descriptionIds []string
	var messageIds []string

	for _, result := range pendingDbResults {
		resultIds = append(resultIds, result.Id)
	}
	for _, desc := range convoMessageDescriptions {
		descriptionIds = append(descriptionIds, desc.Id)
		messageIds = append(messageIds, desc.ConvoMessageId)
	}

	planApply.PlanFileResultIds = resultIds
	planApply.ConvoMessageDescriptionIds = descriptionIds
	planApply.ConvoMessageIds = messageIds

	// Store the PlanApply object
	bytes, err := json.MarshalIndent(planApply, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling plan apply: %v", err)
	}

	appliesDir := getPlanAppliesDir(orgId, planId)
	err = os.MkdirAll(appliesDir, 0755)
	if err != nil {
		return fmt.Errorf("error creating applies dir: %v", err)
	}

	err = os.WriteFile(filepath.Join(appliesDir, planApply.Id+".json"), bytes, 0644)
	if err != nil {
		return fmt.Errorf("error writing plan apply file: %v", err)
	}

	msg := "✅ Marked pending results as applied"

	currentFiles := currentPlanState.CurrentPlanFiles.Files
	var sortedFiles []string
	for path := range currentFiles {

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the wrapped json error to identify the offending field type in the PlanApply struct.
  2. Fix the struct: remove or replace the non-serializable field (channels, funcs, cycles) with a serializable representation.
  3. If a custom MarshalJSON on PlanApply or a nested type is failing, correct or remove it.
  4. Note the apply side-effects may already be persisted (results, descriptions, contexts) but no PlanApply record exists; after fixing, re-run apply or write the record manually if appropriate.
  5. Pin/rollback the recent version change that introduced the new field if an immediate hotfix is not possible.

Example fix

// before: non-serializable field added to PlanApply
type PlanApply struct {
    Id      string
    Results map[string]*PlanFileResult
    onChange func() // json: cannot marshal func
}

// after: keep serializable data only
type PlanApply struct {
    Id      string
    Results map[string]*PlanFileResult
    // callbacks live outside the persisted record
}
Defensive patterns

Strategy: try-catch

Validate before calling

// fail fast on struct changes: verify PlanApply marshals before invoking apply
if _, err := json.Marshal(&db.PlanApply{Id: "smoke", OrgId: orgId, PlanId: planId, UserId: userId, CreatedAt: time.Now()}); err != nil {
    return fmt.Errorf("PlanApply struct not serializable: %w", err)
}

Try / catch

err := db.ApplyPlan(ctx, params)
if err != nil && strings.Contains(err.Error(), "error marshalling plan apply:") {
    // code/schema issue: fix PlanApply struct, not caller input
    return fmt.Errorf("record serialization bug, apply side-effects may be partial: %w", err)
}

Prevention

When it happens

Trigger: Calling ApplyPlan (any params) when the PlanApply struct contains a field json.MarshalIndent cannot encode: an unsupported type added in a code/schema change, a custom MarshalJSON that errors, or cyclic/non-serializable data placed into the record.

Common situations: A recent code change added a new field to PlanApply with a non-serializable type; a custom type's MarshalJSON implementation panics or errors; embedding of an unserializable runtime object into the record after a merge.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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