plandex-ai/plandex · error

error unmarshalling plan settings: %v

Error message

error unmarshalling plan settings: %v

What it means

PlanSettings.DeepCopy wraps a failure from json.Unmarshal when decoding the marshalled copy back into a new PlanSettings. This means the marshalled JSON does not conform to PlanSettings' unmarshalling requirements (e.g. a field's UnmarshalJSON rejected the value, or type mismatch).

Source

Thrown at app/shared/plan_model_settings.go:246

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. Read the wrapped %v error to identify the failing field path from encoding/json
  2. Fix the custom UnmarshalJSON on the offending field to accept valid states or report clearer errors
  3. Since the bytes came from Marshal of the same struct, verify no field's UnmarshalJSON/MarshalJSON are asymmetric
  4. Round-trip test a fully populated PlanSettings in unit tests

Example fix

// before
func (t *T) UnmarshalJSON(b []byte) error { ... if bad { return errors.New("bad") } }
// after
func (t *T) UnmarshalJSON(b []byte) error { ... accept legacy shapes or return nil for empty }
Defensive patterns

Strategy: try-catch

Validate before calling

func validateRoundTrip(ps shared.PlanSettings) error {
    copy, err := ps.DeepCopy()
    if err != nil { return err }
    if copy == nil { return errors.New("nil copy") }
    return nil
}

Try / catch

copy, err := ps.DeepCopy()
if err != nil {
    if strings.Contains(err.Error(), "unmarshalling") {
        return fmt.Errorf("settings round-trip failed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: DeepCopy is invoked via updateModelSettings / ApplyModelSettings and a field's custom UnmarshalJSON returns an error, or a UnmarshalJSON implementation writes into the receiver on invalid input during the marshal→unmarshal round trip.

Common situations: Custom UnmarshalJSON methods on PlanSettings fields that validate data and reject recently added or user-supplied values; mismatches introduced by version skew where the settings schema changed.

Related errors


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