{"record":{"id":"a5f708d229fbe56c","repo":"multica-ai/multica","slug":"encode-cli-config-w","errorCode":null,"errorMessage":"encode CLI config: %w","messagePattern":"encode CLI config: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/config.go","lineNumber":350,"sourceCode":"\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"resolve task-local CLI config root: %w\", err)\n\t\t}\n\t\tfor current := dir; ; current = filepath.Dir(current) {\n\t\t\tif err := os.Chmod(current, 0o700); err != nil {\n\t\t\t\treturn fmt.Errorf(\"restrict task-local CLI config directory: %w\", err)\n\t\t\t}\n\t\t\tif current == root {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tparent := filepath.Dir(current)\n\t\t\tif parent == current {\n\t\t\t\treturn fmt.Errorf(\"task-local CLI config directory %q escapes root %q\", dir, root)\n\t\t\t}\n\t\t}\n\t}\n\tdata, err := json.MarshalIndent(cfg, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encode CLI config: %w\", err)\n\t}\n\n\t// Write to a temp file in the same directory, then rename for atomicity.\n\ttmp, err := os.CreateTemp(dir, \".config-*.json.tmp\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create temp config file: %w\", err)\n\t}\n\ttmpPath := tmp.Name()\n\tif _, err := tmp.Write(append(data, '\\n')); err != nil {\n\t\ttmp.Close()\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"write temp config file: %w\", err)\n\t}\n\tif err := tmp.Close(); err != nil {\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"close temp config file: %w\", err)\n\t}\n\tif err := os.Chmod(tmpPath, 0o600); err != nil {","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/config.go#L332-L368","documentation":"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.","triggerScenarios":"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.","commonSituations":"Developers extending CLIConfig with a logger callback or a channel and forgetting json:\"-\" tags.","solutions":["Tag any non-serializable field with `json:\"-\"` so Marshal skips it","Replace func/chan fields with serializable representations (names, IDs) in the persisted struct","Add a unit test that round-trips CLIConfig through json.Marshal to catch regressions early"],"exampleFix":"// before\ntype CLIConfig struct {\n\tDebug bool `json:\"debug\"`\n\tLog   chan string `json:\"log\"` // unsupported type: chan\n}\n\n// after\ntype CLIConfig struct {\n\tDebug bool `json:\"debug\"`\n\tLog   chan string `json:\"-\"` // runtime-only, never serialized\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := json.Marshal(cfg); err != nil {\n\t// config struct contains unsupported field types; fix before save\n}","preventionTips":["Keep CLIConfig fields limited to JSON-serializable types","Mark runtime-only fields with json:\"-\"","Add a round-trip marshal unit test for CLIConfig"],"tags":["json","serialization","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}