ipfs/kubo · warning

abort failed: close: , remove

Error message

abort failed: close: %w, remove: %v

What it means

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.

Solutions

  1. Call Abort only once and only when Commit was not called (Commit already closes the file)
  2. Check errors.Is(err, fs.ErrClosed) / permission errors on the temp dir and fix filesystem access
  3. If close failed only because the file was already closed, treat as cleanup already done

Example fix

// before
af.Abort()
af.Commit() // or vice versa
// after
if err := af.Commit(); err != nil {
    af.Abort()
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure writable temp location before starting write
if st, err := os.Stat(os.TempDir()); err != nil || !st.IsDir() { /* fix TMPDIR */ }

Try / catch

if err := af.Abort(); err != nil {
    var pe *fs.PathError
    if errors.Is(err, fs.ErrClosed) { /* already cleaned */ } else if errors.As(err, &pe) {
        log.Warnf("abort cleanup incomplete: %v", pe)
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/1d45e4e148ee6dab. Report an issue: GitHub.

Appendix: source

Thrown at repo/fsrepo/migrations/atomicfile/atomicfile.go:67

		// Try to cleanup temp file, but prioritize close error
		_ = os.Remove(f.File.Name())
		return closeErr
	}
	if err := os.Rename(f.File.Name(), f.path); err != nil {
		// The temporary file may hold sensitive data, do not leave it behind.
		_ = os.Remove(f.File.Name())
		return err
	}
	return nil
}

// Abort removes the temporary file without replacing the target
func (f *File) Abort() error {
	closeErr := f.File.Close()
	removeErr := os.Remove(f.File.Name())

	if closeErr != nil && removeErr != nil {
		return fmt.Errorf("abort failed: close: %w, remove: %v", closeErr, removeErr)
	}
	if closeErr != nil {
		return closeErr
	}
	return removeErr
}

// ReadFrom reads from the given reader into the atomic file
func (f *File) ReadFrom(r io.Reader) (int64, error) {
	return io.Copy(f.File, r)
}

View on GitHub (pinned to 329838acdf)