{"record":{"id":"1d45e4e148ee6dab","repo":"ipfs/kubo","slug":"abort-failed-close-w-remove-v","errorCode":null,"errorMessage":"abort failed: close: %w, remove: %v","messagePattern":"abort failed: close: %w, remove: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"repo/fsrepo/migrations/atomicfile/atomicfile.go","lineNumber":67,"sourceCode":"\t\t// Try to cleanup temp file, but prioritize close error\n\t\t_ = os.Remove(f.File.Name())\n\t\treturn closeErr\n\t}\n\tif err := os.Rename(f.File.Name(), f.path); err != nil {\n\t\t// The temporary file may hold sensitive data, do not leave it behind.\n\t\t_ = os.Remove(f.File.Name())\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Abort removes the temporary file without replacing the target\nfunc (f *File) Abort() error {\n\tcloseErr := f.File.Close()\n\tremoveErr := os.Remove(f.File.Name())\n\n\tif closeErr != nil && removeErr != nil {\n\t\treturn fmt.Errorf(\"abort failed: close: %w, remove: %v\", closeErr, removeErr)\n\t}\n\tif closeErr != nil {\n\t\treturn closeErr\n\t}\n\treturn removeErr\n}\n\n// ReadFrom reads from the given reader into the atomic file\nfunc (f *File) ReadFrom(r io.Reader) (int64, error) {\n\treturn io.Copy(f.File, r)\n}\n","sourceCodeStart":49,"sourceCodeEnd":79,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/repo/fsrepo/migrations/atomicfile/atomicfile.go#L49-L79","documentation":"atomicfile.Abort wraps both errors from closing the temp file and removing it when neither succeeds. It reports the two underlying failures (%w preserves closeErr for errors.Is). Abort itself means the target file was never replaced.","triggerScenarios":"Calling Abort() on an already-closed *File (second Abort, or Abort after Commit/close) - Close fails and Remove fails because the name no longer exists on some systems or permissions block it; read-only temp directory; file deleted underneath.","commonSituations":"Error paths in config/migration save code that call both Commit-cleanup and Abort; disk full or permission problems in the repo directory; double-deferred cleanup in migrations.","solutions":["Call Abort only once and only when Commit was not called (Commit already closes the file)","Check errors.Is(err, fs.ErrClosed) / permission errors on the temp dir and fix filesystem access","If close failed only because the file was already closed, treat as cleanup already done"],"exampleFix":"// before\naf.Abort()\naf.Commit() // or vice versa\n// after\nif err := af.Commit(); err != nil {\n    af.Abort()\n}","handlingStrategy":"try-catch","validationCode":"// ensure writable temp location before starting write\nif st, err := os.Stat(os.TempDir()); err != nil || !st.IsDir() { /* fix TMPDIR */ }","typeGuard":null,"tryCatchPattern":"if err := af.Abort(); err != nil {\n    var pe *fs.PathError\n    if errors.Is(err, fs.ErrClosed) { /* already cleaned */ } else if errors.As(err, &pe) {\n        log.Warnf(\"abort cleanup incomplete: %v\", pe)\n    }\n}","preventionTips":["Call Abort exactly once, never after Commit","Check TMPDIR writability before repo writes","Clean up temp dirs when disk is near full"],"tags":["go","filesystem","io","migrations"],"backgroundTag":"temp-file-cleanup-failed","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}