valyala/fasthttp · error

cannot move compressed file from %q to %q: %w

Error message

cannot move compressed file from %q to %q: %w

What it means

After compression and timestamp fix-up succeed, fasthttp atomically moves the temp file into its final compressed-cache location with os.Rename. If the rename fails, the temp file is removed and this error wraps the cause, naming source (temp) and destination paths. Common causes are cross-device renames or a missing/conflicting destination directory.

Source

Thrown at fs.go:1788

		if errf := zw.Flush(); err == nil {
			err = errf
		}
		releaseStacklessZstdWriter(zw, CompressZstdDefault)
	}
	_ = zf.Close()
	_ = f.Close()
	if err != nil {
		_ = os.Remove(tmpFilePath)
		return nil, fmt.Errorf("error when compressing file %q to %q: %w", filePath, tmpFilePath, err)
	}
	if err = os.Chtimes(tmpFilePath, time.Now(), fileInfo.ModTime()); err != nil {
		_ = os.Remove(tmpFilePath)
		return nil, fmt.Errorf("cannot change modification time to %v for tmp file %q: %v",
			fileInfo.ModTime(), tmpFilePath, err)
	}
	if err = os.Rename(tmpFilePath, compressedFilePath); err != nil {
		_ = os.Remove(tmpFilePath)
		return nil, fmt.Errorf("cannot move compressed file from %q to %q: %w", tmpFilePath, compressedFilePath, err)
	}
	return h.newCompressedFSFile(compressedFilePath, fileEncoding)
}

// newCompressedFSFileCache use memory cache compressed files.
func (h *fsHandler) newCompressedFSFileCache(f fs.File, fileInfo fs.FileInfo, filePath, fileEncoding string) (*fsFile, error) {
	var (
		w   = &bytebufferpool.ByteBuffer{}
		err error
	)

	switch fileEncoding {
	case "br":
		zw := acquireStacklessBrotliWriter(w, CompressDefaultCompression)
		_, err = copyZeroAlloc(zw, f)
		if errf := zw.Flush(); err == nil {
			err = errf
		}

View on GitHub (pinned to c96f600972)

Solutions

  1. Ensure the compressed-file directory exists, is writable, and lives on the same filesystem as its temp files (don't split cache across mounts).
  2. Check the wrapped cause: EXDEV means cross-device rename — align temp and cache locations.
  3. Stop external cleaners from deleting the cache directory while the server runs.
  4. Retry the request; the temp file is cleaned up so the next attempt starts fresh.

Example fix

// before: cache on another mount -> EXDEV
fs := &fasthttp.FS{Root: "/srv/www", Compress: true} // tmp in /srv/www, cache target on /mnt/cache
// after: keep cache alongside source files (same fs)
fs := &fasthttp.FS{Root: "/srv/www", Compress: true, CompressedFileSuffix: ".gz"}
Defensive patterns

Strategy: retry

Validate before calling

if fi, err := os.Stat(filepath.Dir(compressedPath)); err != nil || !fi.IsDir() {
    os.MkdirAll(filepath.Dir(compressedPath), 0o755)
}
// ensure same device: compare syscall.Stat_t.Dev of temp dir and target dir

Try / catch

if strings.Contains(err.Error(), "cannot move compressed file") {
    // EXDEV => reconfigure cache to same filesystem; otherwise retry
}

Prevention

When it happens

Trigger: The directory of the compressed cache path was removed between CreateTemp and Rename; the temp dir and target dir reside on different filesystems (Rename cannot cross devices); another process holds the target path in a way that makes rename fail (rare on POSIX).

Common situations: CachePath pointing to a different mounted volume than the temp file location; cache dir deleted by cleanup while under load; permission loss on the destination directory mid-run.

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/fb67beeccc406cc7. Report an issue: GitHub.