multica-ai/multica · error

encode CLI config: %w

Error message

encode CLI config: %w

What it means

json.MarshalIndent failed while serializing CLIConfig before writing. For a plain struct config this is practically unreachable unless the struct contains a channel, func, or circular reference — none of which the CLI config is expected to have. Seeing it usually means the struct definition was extended with a non-serializable field.

Source

Thrown at server/internal/cli/config.go:350

		if err != nil {
			return fmt.Errorf("resolve task-local CLI config root: %w", err)
		}
		for current := dir; ; current = filepath.Dir(current) {
			if err := os.Chmod(current, 0o700); err != nil {
				return fmt.Errorf("restrict task-local CLI config directory: %w", err)
			}
			if current == root {
				break
			}
			parent := filepath.Dir(current)
			if parent == current {
				return fmt.Errorf("task-local CLI config directory %q escapes root %q", dir, root)
			}
		}
	}
	data, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return fmt.Errorf("encode CLI config: %w", err)
	}

	// Write to a temp file in the same directory, then rename for atomicity.
	tmp, err := os.CreateTemp(dir, ".config-*.json.tmp")
	if err != nil {
		return fmt.Errorf("create temp config file: %w", err)
	}
	tmpPath := tmp.Name()
	if _, err := tmp.Write(append(data, '\n')); err != nil {
		tmp.Close()
		os.Remove(tmpPath)
		return fmt.Errorf("write temp config file: %w", err)
	}
	if err := tmp.Close(); err != nil {
		os.Remove(tmpPath)
		return fmt.Errorf("close temp config file: %w", err)
	}
	if err := os.Chmod(tmpPath, 0o600); err != nil {

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Tag any non-serializable field with `json:"-"` so Marshal skips it
  2. Replace func/chan fields with serializable representations (names, IDs) in the persisted struct
  3. Add a unit test that round-trips CLIConfig through json.Marshal to catch regressions early

Example fix

// before
type CLIConfig struct {
	Debug bool `json:"debug"`
	Log   chan string `json:"log"` // unsupported type: chan
}

// after
type CLIConfig struct {
	Debug bool `json:"debug"`
	Log   chan string `json:"-"` // runtime-only, never serialized
}
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := json.Marshal(cfg); err != nil {
	// config struct contains unsupported field types; fix before save
}

Prevention

When it happens

Trigger: Adding a field of type chan, func, or complex to CLIConfig and then calling SaveCLIConfig; a map with non-string keys or a self-referencing type added during development.

Common situations: Developers extending CLIConfig with a logger callback or a channel and forgetting json:"-" tags.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/a5f708d229fbe56c. Report an issue: GitHub.