sipeed/picoclaw · error

failed to serialize config: %w

Error message

failed to serialize config: %w

What it means

json.Marshal of the normalized config failed (helpers.go:113-116). The config structs are plain data, so encoding/json only fails if a field type it cannot handle (chan, func, unsafe cyclic pointers) was introduced into config.Config — effectively a code regression in pkg/config, not a user-input problem.

Source

Thrown at cmd/picoclaw/internal/mcp/helpers.go:115

func loadConfig() (*config.Config, error) {
	cfg, err := config.LoadConfig(internal.GetConfigPath())
	if err != nil {
		return nil, fmt.Errorf("failed to load config: %w", err)
	}
	return cfg, nil
}

func saveValidatedConfig(cfg *config.Config) error {
	if cfg == nil {
		return fmt.Errorf("config is nil")
	}

	normalizedCfg := normalizedConfigForSave(cfg)

	data, err := json.Marshal(normalizedCfg)
	if err != nil {
		return fmt.Errorf("failed to serialize config: %w", err)
	}

	if err := validateConfigDocument(data); err != nil {
		return err
	}

	if err := config.SaveConfig(internal.GetConfigPath(), normalizedCfg); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	return nil
}

func normalizedConfigForSave(cfg *config.Config) *config.Config {
	clone := *cfg
	if cfg.Tools.MCP.Servers == nil {
		return &clone
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Revert or redesign the offending field: keep config types to strings, numbers, bools, slices, and maps
  2. If a live object is needed, mark it json:"-" so it is excluded from serialization

Example fix

// before
type MCPServerConfig struct {
	Client *mcp.Client `json:"client"` // cyclic/non-serializable
}
// after
type MCPServerConfig struct {
	Client *mcp.Client `json:"-"` // excluded from config serialization
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := saveValidatedConfig(cfg); err != nil {
	if strings.Contains(err.Error(), "failed to serialize config") {
		// internal: a config field type broke encoding/json;
		// fail loudly and file a bug rather than retrying
		return fmt.Errorf("config struct no longer JSON-serializable: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Someone adds a func or channel field to Config/MCPConfig/MCPServerConfig; a cyclic reference is created between config types.

Common situations: Refactors of pkg/config that attach live objects (loggers, clients) instead of serializable settings.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/da2d84514d2fd89c. Report an issue: GitHub.