{"record":{"id":"2038921a81107b2a","repo":"alibaba/open-code-review","slug":"marshal-session-end-w","errorCode":null,"errorMessage":"marshal session_end: %w","messagePattern":"marshal session_end: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/session/persist.go","lineNumber":399,"sourceCode":"\t\t\"duration_seconds\": duration.Seconds(),\n\t\t\"llm_failures\":     llmFailures,\n\t}\n\tif manifest != nil {\n\t\trec[\"run_manifest\"] = manifest\n\t}\n\tjw.lastUUID = uuid\n\n\t// Marshal explicitly (not via writeRecordLocked) so a marshal failure on the\n\t// final record is reported rather than swallowed.\n\tdata, err := json.Marshal(rec)\n\tif err != nil {\n\t\tif jw.writer != nil {\n\t\t\tjw.writer.Flush()\n\t\t}\n\t\tif jw.file != nil {\n\t\t\tjw.file.Close()\n\t\t}\n\t\treturn fmt.Errorf(\"marshal session_end: %w\", err)\n\t}\n\n\tvar writeErr error\n\tif jw.writer != nil {\n\t\tif _, err := jw.writer.Write(data); err != nil {\n\t\t\twriteErr = fmt.Errorf(\"write session_end: %w\", err)\n\t\t} else if err := jw.writer.WriteByte('\\n'); err != nil {\n\t\t\twriteErr = fmt.Errorf(\"write session_end: %w\", err)\n\t\t} else if err := jw.writer.Flush(); err != nil {\n\t\t\twriteErr = fmt.Errorf(\"flush session_end: %w\", err)\n\t\t}\n\t}\n\tif jw.file != nil {\n\t\tif err := jw.file.Close(); err != nil && writeErr == nil {\n\t\t\twriteErr = fmt.Errorf(\"close session file: %w\", err)\n\t\t}\n\t}\n\treturn writeErr","sourceCodeStart":381,"sourceCodeEnd":417,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/session/persist.go#L381-L417","documentation":"WriteSessionEnd marshals the final session_end record explicitly so a JSON marshal failure on the closing record is reported rather than swallowed. Before returning the error it flushes the buffered writer and closes the file, leaving the session file without its session_end record. json.Marshal of a map of plain values should not fail in practice, so this usually indicates a custom/unsupported value (e.g. NaN/Inf float, channel, func) was injected into the record fields.","triggerScenarios":"WriteSessionEnd builds the rec map (uuid, counts, files_reviewed, duration_seconds, llm_failures, optional run_manifest) and json.Marshal(rec) returns an error; WriteSessionEnd is the last call before the session file is finalized.","commonSituations":"Passing a RunManifest containing non-marshalable values (func, channel, or invalid float like NaN in custom metrics); future code changes adding unsupported types to the record.","solutions":["Inspect the wrapped error for the offending Go type ('json: unsupported type: ...') and fix the value passed into WriteSessionEnd/RunManifest","Sanitize floats (reject/convert NaN and +Inf) before adding them to the manifest","Add MarshalJSON methods or change fields to marshalable types in RunManifest","Note the file was already flushed and closed by the error path — do not reuse the writer afterward; create a new session"],"exampleFix":"// before\nrec[\"score\"] = math.NaN()\n// after\nif math.IsNaN(score) { score = 0 }\nrec[\"score\"] = score","handlingStrategy":"try-catch","validationCode":"// pre-validate manifest values are marshalable\nif _, err := json.Marshal(manifest); err != nil {\n    return fmt.Errorf(\"run manifest not marshalable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := writer.WriteSessionEnd(); err != nil {\n    if strings.Contains(err.Error(), \"marshal session_end\") {\n        log.Printf(\"session_end record not written; file already closed: %v\", err)\n        // do not reuse this writer; start a new session\n    }\n}","preventionTips":["Never put NaN/Inf floats, funcs, or channels into run manifest fields","Test WriteSessionEnd once with a full manifest in unit tests","Remember the error path closes the file: treat the session as finalized either way"],"tags":["json","serialization","session-persistence"],"backgroundTag":"json-marshal-failed","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}