{"record":{"id":"130f3e98063acc28","repo":"alibaba/open-code-review","slug":"close-session-file-w","errorCode":null,"errorMessage":"close session file: %w","messagePattern":"close session file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/session/persist.go","lineNumber":414,"sourceCode":"\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\n}\n\nfunc (jw *jsonlWriter) flushAndClose() {\n\tjw.mu.Lock()\n\tdefer jw.mu.Unlock()\n\tif jw.writer != nil {\n\t\tjw.writer.Flush()\n\t}\n\tif jw.file != nil {\n\t\tjw.file.Close()\n\t}\n}\n","sourceCodeStart":396,"sourceCodeEnd":430,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/session/persist.go#L396-L430","documentation":"After writing the session_end record, WriteSessionEnd closes the session file; a non-nil Close() error is reported as 'close session file' (only if no earlier write error was recorded, so the original cause is preserved). The record itself may have been written correctly — the failure is in committing the file handle back to the OS.","triggerScenarios":"WriteSessionEnd: jw.file.Close() returns an error while writeErr was nil — e.g. buffered data was already fine but close-time flush to physical media failed (NFS), or the fd was already closed elsewhere.","commonSituations":"Double-close (Close called both by flushAndClose and WriteSessionEnd on the same writer); NFS/fuse filesystems reporting delayed-write errors at close; fd exhaustion cleanup races.","solutions":["Check the wrapped error: 'file already closed' (os.ErrClosed) means a double-close — ensure flushAndClose and WriteSessionEnd are not both invoked on the same jsonlWriter","On NFS, treat close errors as real write failures: verify file integrity or write sessions to local storage","Call errors.Is(err, os.ErrClosed) to distinguish double-close from genuine I/O failure","If the session_end line is present and complete, the data is intact despite the close error"],"exampleFix":"// before\njw.flushAndClose()\nif err := w.WriteSessionEnd(...); err != nil {...} // double close\n// after\nif err := w.WriteSessionEnd(...); err != nil {...} // WriteSessionEnd performs the close","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := writer.WriteSessionEnd(); err != nil {\n    if strings.Contains(err.Error(), \"close session file\") {\n        if errors.Is(err, os.ErrClosed) {\n            log.Printf(\"double close bug: %v\", err)\n        } else {\n            log.Printf(\"possible data loss on close: %v\", err)\n        }\n    }\n}","preventionTips":["Do not call flushAndClose after WriteSessionEnd — it already closes","Treat close errors on NFS as real write failures and verify file contents","Use errors.Is(err, os.ErrClosed) to classify close failures"],"tags":["filesystem","file-close","session-persistence"],"backgroundTag":"file-close-failed","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}