nats-io/nats-server · error

failed to close temporary file

Error message

failed to close temporary file: %w

What it means

Returned when closing the temporary block file fails at the end of a file-store block update. Data was written and synced, but Close returned an error (often masking deferred flush issues), so the update aborts via errorCleanup.

Solutions

  1. Check open file descriptor limits (ulimit -n) and raise them if exhausted.
  2. Look for earlier I/O errors on the volume; Close often surfaces deferred write failures.
  3. Verify the container/host storage driver supports standard POSIX file semantics.
  4. Retry the operation after resolving the resource issue.

Example fix

// before: default low limit
// after: raise fd limit (systemd)
// LimitNOFILE=1048576
Defensive patterns

Strategy: validation

Validate before calling

// check fd headroom before high-throughput writes
if fds, err := countOpenFDs(pid); err == nil && float64(fds)/float64(fdLimit) > 0.8 {
    // raise LimitNOFILE or reduce concurrent streams
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to close temporary file") {
    // usually fd exhaustion or deferred I/O error; check ulimit and disk
}

Prevention

When it happens

Trigger: tmpFD.Close() returns an error after a successful write and Sync of new block data during a message block update.

Common situations: File descriptor exhaustion (too many open files), filesystem state issues after earlier I/O errors, or storage driver problems on virtualized/containerized filesystems.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/1c821dc9bafa2f93. Report an issue: GitHub.

Appendix: source

Thrown at server/filestore.go:8070

	}

	// Re-encrypt the block if necessary.
	if err = mb.encryptOrDecryptIfNeeded(buf); err != nil {
		return errorCleanup(err)
	}

	// Write the new block data (which might be compressed or encrypted) to the
	// temporary file.
	if n, err := tmpFD.Write(buf); err != nil {
		return errorCleanup(fmt.Errorf("failed to write to temporary file: %w", err))
	} else if n != len(buf) {
		return errorCleanup(fmt.Errorf("short write to temporary file (%d != %d)", n, len(buf)))
	}
	if err := tmpFD.Sync(); err != nil {
		return errorCleanup(fmt.Errorf("failed to sync temporary file: %w", err))
	}
	if err := tmpFD.Close(); err != nil {
		return errorCleanup(fmt.Errorf("failed to close temporary file: %w", err))
	}

	// Now replace the original file with the newly updated temp file.
	if err := os.Rename(tmpFN, origFN); err != nil {
		return fmt.Errorf("failed to move temporary file into place: %w", err)
	}

	// Since the message block might be retained in memory, make sure the
	// compression algorithm is up-to-date, since this will be needed when
	// compacting or truncating.
	mb.cmp = alg

	// Also update rbytes
	mb.rbytes = uint64(len(buf))

	return nil
}

View on GitHub (pinned to 3a66a489d2)