valyala/fasthttp · warning

cannot change modification time to %v for tmp file %q: %v

Error message

cannot change modification time to %v for tmp file %q: %v

What it means

After compressing into a temp file, fasthttp calls os.Chtimes to set the temp file's modification time to the original file's modtime so HTTP caching (Last-Modified/ETag) stays consistent. If Chtimes fails, the temp file is removed and this error is returned with the intended modtime and temp path. This usually means the temp file vanished or the filesystem disallows timestamp changes.

Source

Thrown at fs.go:1783

		}
		releaseStacklessGzipWriter(zw, CompressDefaultCompression)
	case "zstd":
		zw := acquireStacklessZstdWriter(zf, CompressZstdDefault)
		_, err = copyZeroAlloc(zw, f)
		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":

View on GitHub (pinned to c96f600972)

Solutions

  1. Exclude fasthttp's compressed-file cache directory from external cleanup daemons (tmpwatch, systemd-tmpfiles).
  2. Check the wrapped cause; if it's 'operation not supported', move cache files to a local filesystem (ext4/tmpfs).
  3. Retry the request: the failed temp file is removed and the next attempt recompresses.
  4. If seccomp/apparmor blocks utimensat, adjust the container security profile.

Example fix

// before: cleaner racing with cache
// systemd-tmpfiles rule purging the whole webroot hourly
// after: exclude *.fasthttp.gz / cache dir from cleanup, or use local cache dir
fs := &fasthttp.FS{Root: "/mnt/net-assets", Compress: true} // move CachePath to local disk:
fs.CachePath = "/var/cache/app/fs"
Defensive patterns

Strategy: retry

Validate before calling

// ensure the cache filesystem supports utimes before enabling compression:
if err := os.Chtimes(testFile, time.Now(), time.Now()); err != nil {
    // disable compression or relocate cache to local fs
}

Try / catch

if strings.Contains(err.Error(), "cannot change modification time") {
    // transient; safe to retry the request, temp file was cleaned up
}

Prevention

When it happens

Trigger: os.Chtimes failing on the just-created temp file during on-demand compression — typically because the temp file was deleted concurrently (external cleaner), the filesystem does not support utimes (some network/FUSE mounts), or an OS-level error (EINVAL on exotic fs).

Common situations: tmpwatch/systemd-tmpfiles cleaners racing with compression in cache dirs; FUSE/NFS mounts lacking utimes support; containers with restricted syscalls (seccomp blocking utimensat).

Related errors


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