{"record":{"id":"8c740fe5a93e7ad7","repo":"shadow1ng/fscan","slug":"failed-to-marshal-result-w","errorCode":null,"errorMessage":"failed to marshal result: %w","messagePattern":"failed to marshal result: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/output/writers.go","lineNumber":525,"sourceCode":"func (w *JSONWriter) Write(result *ScanResult) error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.closed {\n\t\treturn fmt.Errorf(\"writer is closed\")\n\t}\n\tif result == nil {\n\t\treturn fmt.Errorf(\"result cannot be nil\")\n\t}\n\n\t// 1. 加入内存分类缓冲（用于最终有序输出）\n\tw.buffer.Add(result)\n\n\t// 2. 实时写入备份文件（NDJSON格式，防崩溃丢失）\n\tif w.realtimeFile != nil {\n\t\tdata, err := json.Marshal(result)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to marshal result: %w\", err)\n\t\t}\n\t\tif _, err := w.realtimeFile.Write(append(data, '\\n')); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write realtime backup: %w\", err)\n\t\t}\n\t\tif err := w.realtimeFile.Sync(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to sync realtime backup: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Flush 刷新写入器\nfunc (w *JSONWriter) Flush() error {\n\treturn nil\n}\n\n// Close 关闭写入器（写入完整JSON，删除临时备份）","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/common/output/writers.go#L507-L543","documentation":"JSONWriter.Write marshals the ScanResult to JSON for the realtime NDJSON backup. This error is returned when json.Marshal fails. For a plain ScanResult this is rare and usually indicates an unmarshalable field (e.g. a channel, func, or cyclic reference added to the struct).","triggerScenarios":"Calling Write() with a ScanResult whose struct contains fields json.Marshal cannot encode: channels, funcs, cycles, or a custom MarshalJSON returning an error.","commonSituations":"Extending ScanResult with non-serializable runtime state (connections, callbacks); a custom type's MarshalJSON implementation erroring; embedding a raw map with unencodable keys/values.","solutions":["Remove or convert unmarshalable fields (channels/funcs/cycles) on ScanResult","Check any custom MarshalJSON methods on result fields for returned errors","Use json.Marshal on a sanitized/dto copy of the result","Update the library if a recent ScanResult change introduced the bad field","Reproduce with json.Marshal(result) standalone to identify the failing field"],"exampleFix":"// before\ntype ScanResult struct {\n\tHost string\n\tConn net.Conn // not marshalable\n}\n// after\ntype ScanResult struct {\n\tHost string\n}\nfunc (r ScanResult) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(map[string]string{\"host\": r.Host})\n}","handlingStrategy":"validation","validationCode":"func serializable(r *ScanResult) error {\n\t_, err := json.Marshal(r)\n\treturn err\n}\n// call before writer.Write():\nif err := serializable(result); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := writer.Write(result); err != nil {\n\tif strings.Contains(err.Error(), \"failed to marshal result\") {\n\t\tlog.Printf(\"unmarshalable ScanResult (check custom MarshalJSON / new fields): %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Keep ScanResult free of channels, funcs, and cyclic references","Test json.Marshal(ScanResult{}) in unit tests after struct changes","Review any custom MarshalJSON implementations for error paths","Use dto copies for serialization when runtime state must live on the struct","Add a CI check that round-trips a fully populated ScanResult through JSON"],"tags":["go","json","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}