{"record":{"id":"d432ac0b288db314","repo":"sipeed/picoclaw","slug":"marshal-result-w","errorCode":null,"errorMessage":"marshal result: %w","messagePattern":"marshal result: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/membench/eval.go","lineNumber":275,"sourceCode":"\treturn AggMetrics{\n\t\tOverallF1:      overallF1,\n\t\tOverallHitRate: totalHitRate / float64(nHit),\n\t\tByCategory:     byCat,\n\t\tTotalQuestions: len(qaResults),\n\t\tValidF1Count:   validF1Count,\n\t}\n}\n\n// SaveResults writes per-sample eval results to JSON files.\nfunc SaveResults(results []EvalResult, outDir string) error {\n\tif err := os.MkdirAll(outDir, 0o755); err != nil {\n\t\treturn fmt.Errorf(\"create output dir: %w\", err)\n\t}\n\tfor _, r := range results {\n\t\tpath := filepath.Join(outDir, fmt.Sprintf(\"eval_%s_%s.json\", r.Mode, r.SampleID))\n\t\tdata, err := json.MarshalIndent(r, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"marshal result: %w\", err)\n\t\t}\n\t\tif err := os.WriteFile(path, data, 0o644); err != nil {\n\t\t\treturn fmt.Errorf(\"write result: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// SaveAggregated writes a combined results.json with all modes.\nfunc SaveAggregated(results []EvalResult, outDir string) error {\n\tbyMode := map[string][]EvalResult{}\n\tfor _, r := range results {\n\t\tbyMode[r.Mode] = append(byMode[r.Mode], r)\n\t}\n\n\taggMap := map[string]AggMetrics{}\n\tfor mode, modeResults := range byMode {\n\t\taggMap[mode] = computeModeAgg(modeResults)","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/membench/eval.go#L257-L293","documentation":"json.MarshalIndent failure inside SaveResults (cmd/membench/eval.go). Each EvalResult must serialize to JSON; Go's encoding/json errors on unsupported values — NaN/Inf floats, channels, funcs, or cycles. Given EvalResult carries numeric metrics, the realistic cause is a NaN score produced when a metric divides by zero (e.g. F1 with no true positives and no predictions).","triggerScenarios":"A question category where every sample yields NaN F1 (precision/recall both 0/0) and that NaN reaches the struct; adding a new field of type chan or func to EvalResult; Inf token counts from a parsing bug.","commonSituations":"Eval runs where a mode answers nothing for a category; upstream schema change adding non-serializable fields; corpus edge case producing 0/0 division.","solutions":["Sanitize metrics before saving: replace NaN/Inf with 0 (math.IsNaN/math.IsInf)","Fix the root cause: guard division so F1 is 0 when denominator is 0 instead of NaN","Inspect the failing EvalResult by logging r.SampleID/r.Mode before Marshal","Never add func/chan-typed fields to structs passed to MarshalIndent"],"exampleFix":"// before\nbyCat[cat] = f1 // f1 may be NaN (0/0)\n\n// after\nif math.IsNaN(f1) || math.IsInf(f1, 0) {\n    f1 = 0\n}\nbyCat[cat] = f1","handlingStrategy":"validation","validationCode":"func sanitizeFloats(m AggMetrics) AggMetrics {\n    fix := func(v float64) float64 {\n        if math.IsNaN(v) || math.IsInf(v, 0) {\n            return 0\n        }\n        return v\n    }\n    m.OverallF1 = fix(m.OverallF1)\n    m.OverallHitRate = fix(m.OverallHitRate)\n    for k, v := range m.ByCategory {\n        v.F1 = fix(v.F1)\n        m.ByCategory[k] = v\n    }\n    return m\n}","typeGuard":null,"tryCatchPattern":"if data, err := json.MarshalIndent(r, \"\", \"  \"); err != nil {\n    return fmt.Errorf(\"marshal result for %s/%s (likely NaN metric): %w\", r.Mode, r.SampleID, err)\n}","preventionTips":["Guard every division: return 0 when the denominator is 0","Never let NaN/Inf reach structs passed to encoding/json — it errors, doesn't encode null","Keep only string/number/bool/slice/map fields in result structs","Add a golden-file unit test that marshals a fully populated EvalResult"],"tags":["go","json","benchmark","nan"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}