nats-io/nats-server · critical

failed to move temporary file into place

Error message

failed to move temporary file into place: %w

What it means

Returned when os.Rename(tmpFN, origFN) fails while swapping the updated temporary block file into place after a successful write/sync/close. The new block data is fully written but could not atomically replace the original file, so the update fails with this wrapped error.

Solutions

  1. Ensure the entire JetStream store directory is on the same filesystem (no split symlinks/bind mounts).
  2. Verify write permission on the stream's data directory for the nats process user.
  3. Stop backup/AV agents from holding or removing files under the store directory mid-update.
  4. Retry the update once the directory state is fixed; the temp file may need manual removal if errorCleanup missed it.

Example fix

// before: split filesystems
StoreDir: /data/jetstream  # with symlink to /mnt/other/blockdir
// after: single filesystem
StoreDir: /data/jetstream  # entire tree on one volume
Defensive patterns

Strategy: validation

Validate before calling

// ensure store dir is one filesystem and writable before starting
info1, _ := os.Stat(storeDir)
dev := info1.Sys().(*syscall.Stat_t).Dev
if symlinkedAcrossDevices(storeDir) {
    return fmt.Errorf("store dir spans multiple filesystems")
}
if !writable(storeDir) {
    return fmt.Errorf("no write permission on %s", storeDir)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to move temporary file into place") {
    // check for EXDEV / permission issues; may need to remove leftover temp files
}

Prevention

When it happens

Trigger: os.Rename fails during the final commit step of a message block update — replacing the original block file with the temp file.

Common situations: Temp file and target on different filesystems (store dir partially relocated or symlinked), missing write permission on the directory (rename requires dir write), the original file removed/locked by backup agents, or cross-device link (EXDEV) errors in container volume setups.

Related errors


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

Appendix: source

Thrown at server/filestore.go:8075

	}

	// 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
}

// Lock should be held.
func (mb *msgBlock) decompressIfNeeded(buf []byte) ([]byte, error) {
	var meta CompressionInfo
	if n, err := meta.UnmarshalMetadata(buf); err != nil {
		// There was a problem parsing the metadata header of the block.

View on GitHub (pinned to 3a66a489d2)