wavetermdev/waveterm · error

failed to rename temp file %q to %q: %w (also failed to remo

Error message

failed to rename temp file %q to %q: %w (also failed to remove temp file: %v)

What it means

AtomicWriteFile writes to a temp file and then renames it onto the target. This error is returned when os.Rename fails and the subsequent attempt to remove the temp file also fails, wrapping both errors. The original target file is left untouched; a stale .tmp file may remain.

Source

Thrown at pkg/util/fileutil/fileutil.go:189

	}
	ext := strings.ToLower(filepath.Ext(path))
	if mimeType, ok := StaticMimeTypeMap[ext]; ok {
		return mimeType
	}
	return ""
}

func AtomicWriteFile(fileName string, data []byte, perm os.FileMode) error {
	tmpFileName := fileName + TempFileSuffix
	if err := os.WriteFile(tmpFileName, data, perm); err != nil {
		if removeErr := os.Remove(tmpFileName); removeErr != nil && !os.IsNotExist(removeErr) {
			return fmt.Errorf("failed to write temp file %q: %w (also failed to remove temp file: %v)", tmpFileName, err, removeErr)
		}
		return err
	}
	if err := os.Rename(tmpFileName, fileName); err != nil {
		if removeErr := os.Remove(tmpFileName); removeErr != nil && !os.IsNotExist(removeErr) {
			return fmt.Errorf("failed to rename temp file %q to %q: %w (also failed to remove temp file: %v)", tmpFileName, fileName, err, removeErr)
		}
		return err
	}
	return nil
}

var (
	systemBinDirs = []string{
		"/bin/",
		"/usr/bin/",
		"/usr/local/bin/",
		"/opt/bin/",
		"/sbin/",
		"/usr/sbin/",
	}
	suspiciousPattern = regexp.MustCompile(`[:;#!&$\t%="|>{}]`)
	flagPattern       = regexp.MustCompile(` --?[a-zA-Z0-9]`)
)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure the target path is not a directory and its parent has write permission.
  2. Ensure the temp file and target are on the same filesystem (write temp next to the target, which this function already does — so check for mount-point changes).
  3. Manually remove the stale fileName+".tmp" file, then retry.
  4. Check for processes locking the target (editors, sync clients, antivirus) and retry.

Example fix

// before
fileutil.AtomicWriteFile("/home/user/.config/wave", data, 0644) // ~/.config/wave is a directory

// after
if fi, err := os.Stat(target); err == nil && fi.IsDir() {
    return fmt.Errorf("target %s is a directory", target)
}
return fileutil.AtomicWriteFile(target, data, 0644)
Defensive patterns

Strategy: retry

Validate before calling

// ensure target is not a directory and temp file is absent
if fi, err := os.Stat(fileName); err == nil && fi.IsDir() {
    return fmt.Errorf("target %s is a directory", fileName)
}
os.Remove(fileName + ".tmp") // ignore NotExist

Try / catch

// Go: retry rename failures after cleanup
if err := fileutil.AtomicWriteFile(f, data, 0644); err != nil {
    if strings.Contains(err.Error(), "failed to rename temp file") {
        os.Remove(f + ".tmp")
        time.Sleep(50 * time.Millisecond)
        err = fileutil.AtomicWriteFile(f, data, 0644) // retry once
    }
}

Prevention

When it happens

Trigger: os.Rename(tmpFileName, fileName) failing (target path is a directory, cross-device rename, target locked/permission issue) combined with os.Remove(tmpFileName) failing for a reason other than NotExist.

Common situations: Target path exists as a directory; /tmp-style cross-filesystem setups when temp and target are on different mounts; an open file handle or AV scanner holding the temp file on Windows; permission mismatch between temp and target directories.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/c7853f5ffb441e5c. Report an issue: GitHub.