plandex-ai/plandex · error

error marshalling plan settings: %v

Error message

error marshalling plan settings: %v

What it means

PlanSettings.DeepCopy serializes PlanSettings to JSON and back to produce a deep copy. This error wraps a failure in the json.Marshal step, meaning PlanSettings contains a value Go's encoding/json cannot serialize (e.g. an unsupported type like chan/func, a cyclic pointer, or a custom MarshalJSON returning an error).

Source

Thrown at app/shared/plan_model_settings.go:241

func (ps *PlanSettings) Equals(other *PlanSettings) bool {
	return ps.GetModelPack().Equals(other.GetModelPack())
}

func (ps PlanSettings) ForCompare() PlanSettings {
	ps.UpdatedAt = time.Time{}
	ps.CustomModelPacks = nil
	ps.CustomModels = nil
	ps.CustomProviders = nil
	ps.IsCloud = false
	ps.Configured = false
	return ps
}

func (ps PlanSettings) DeepCopy() (*PlanSettings, error) {
	bytes, err := json.Marshal(ps)
	if err != nil {
		return nil, fmt.Errorf("error marshalling plan settings: %v", err)
	}
	var copy PlanSettings
	err = json.Unmarshal(bytes, &copy)
	if err != nil {
		return nil, fmt.Errorf("error unmarshalling plan settings: %v", err)
	}
	return &copy, nil
}

func getOptionalModelProviderOptions(settings *PlanSettings, cfg *ModelRoleConfig) ModelProviderOptions {
	if cfg == nil {
		return ModelProviderOptions{}
	}
	return cfg.GetModelProviderOptions(settings)
}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the wrapped %v error to find the offending field/value reported by encoding/json
  2. Fix or remove the unmarshalable field (chan/func/cyclic pointer) in PlanSettings or give it a `json:"-"` tag
  3. Implement or fix the custom MarshalJSON method on the failing type
  4. Add a unit test that round-trips a fully populated PlanSettings through DeepCopy

Example fix

// before
Timeout chan time.Time `json:"timeout"`
// after
Timeout chan time.Time `json:"-"`
Defensive patterns

Strategy: validation

Validate before calling

func validateMarshalable(ps shared.PlanSettings) error {
    _, err := json.Marshal(ps)
    return err
}
// call validateMarshalable(ps) before updateModelSettings/ApplyModelSettings

Try / catch

copy, err := ps.DeepCopy()
if err != nil {
    if strings.Contains(err.Error(), "marshalling") {
        // log and reject settings change; keep previous settings
        return fmt.Errorf("settings not serializable: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling updateModelSettings or ApplyModelSettings (or any caller of DeepCopy) when the PlanSettings struct has been populated with unmarshalable field values (chan, func, complex, or a field whose MarshalJSON errors).

Common situations: A developer added a new field to PlanSettings with an unsupported type, or a nested struct's custom MarshalJSON fails on transiently invalid data; also seen when plugin/extension code stores arbitrary objects in settings.

Related errors


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