What is the problem you are having with rclone?
On a Windows rclone mount with --vfs-cache-mode writes, a save made atomically — write a temp file, then rename it over the target, which is how many tools save (editors, VS Code, Node.js fs, Chrome downloads, and notably AI coding tools like Claude Code) can be silently lost. The application is told the save succeeded, but the file keeps its old contents.
I reproduced it deterministically and traced it, and I think the core issue is small and specific. I'd love a sanity check before I file a GitHub issue (or send a PR).
The key finding: when the target has any open handle at rename time, rclone's local cache-file rename fails on Windows (os.Rename/MoveFileEx cannot rename over an open handle), rclone logs it at INFO but then returns errc=0 (success) to the kernel — so the caller believes the save worked and the new bytes are dropped. From a captured -vv log:
… /sub/edittest.md.tmp.100136.5d2f8ba93ef7: CreateEx: flags=0x502, mode=0770 # tool writes its temp file
… /sub/edittest.md.tmp.100136.5d2f8ba93ef7: Rename: newPath="/sub/edittest.md" # …then renames it over the target
… INFO : sub/edittest.md: File.Rename failed in Cache: failed to rename in cache:
…\repro-cache\vfs\RPi\…\sub\edittest.md.tmp.100136.5d2f8ba93ef7 to
…\repro-cache\vfs\RPi\…\sub\edittest.md: rename …: Access is denied.
… /sub/edittest.md.tmp.100136.5d2f8ba93ef7: >Rename: errc=0 # <-- returns SUCCESS despite the failure
… /sub/edittest.md: >OpenEx: errc=-5, fh=0xFFFFFFFFFFFFFFFF # next read then EIOs
… INFO : sub/edittest.md: vfs cache: upload succeeded try #1 # old content re-uploaded
So two things go wrong: (1) the cache rename can't happen while the target handle is open, and (2) that failure is swallowed and reported as success. Even if (1) is hard to avoid on Windows, (2) turns it into silent data loss rather than a retryable error.
Root cause (from reading the source): the write-back queue rename (vfs/vfscache/writeback/writeback.go Rename) already handles renames correctly (it cancels an in-flight upload and re-queues under the new name). The loss is one layer down: vfs/vfscache/cache.go Cache.Rename → vfs/vfscache/item.go item.rename() calls the local rename() (→ os.Rename) on the cache file without first closing item.fd or waiting for the in-flight write-back uploader to release its handle. On POSIX, rename(2) replaces an open file fine (so this is invisible on Linux/macOS); on Windows MoveFileEx returns Access is denied, and the error is logged but not propagated.
This is the Windows-reproducible core of the older #4293 ("vfs: losing data when doing writeback", closed as not planned).
Why this is increasingly worth fixing
The atomic temp→rename save pattern is now the default in a lot of tooling, and specifically in AI coding assistants. Claude Code changed its Edit/Write file tools to write a temp file and atomically rename it over the target (around v2.1.178, as the fix for "Write/Edit producing 0-byte or truncated files on network drives" in v2.1.181). That change means every edit Claude Code makes to a file on an rclone Windows mount can hit this: the tool reports the edit succeeded, but the file silently reverts, which is exactly how I found this. As these tools proliferate, more people editing code on rclone Windows mounts will hit silent data loss with no error to tell them.
Minimal, deterministic reproduction
The failure needs an open handle on the target when the atomic rename lands. In normal use that handle is held by the in-flight write-back upload, the post-close grace period, antivirus, or a dropped-connection re-stat, all timing-dependent. To make it deterministic, just hold a read handle open yourself. On a Windows rclone mount (--vfs-cache-mode writes), drive R::
Add-Type @"
using System; using System.Runtime.InteropServices;
public class W { [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
public static extern bool MoveFileEx(string a, string b, int flags); }
"@
[IO.File]::WriteAllText("R:\t.txt","baseline"); Start-Sleep 3
# (a) atomic replace with NO open handle -> succeeds
[IO.File]::WriteAllText("R:\t.txt.tmp1","v1")
[W]::MoveFileEx("R:\t.txt.tmp1","R:\t.txt",1) # 1 = MOVEFILE_REPLACE_EXISTING => True
# (b) same atomic replace WHILE a read handle is open on the target -> fails
$fs=[IO.File]::Open("R:\t.txt",[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::ReadWrite)
[IO.File]::WriteAllText("R:\t.txt.tmp2","v2")
[W]::MoveFileEx("R:\t.txt.tmp2","R:\t.txt",1) # => False, GetLastError = "Access is denied"
$fs.Close()
Result here: (a) True, (b) False / Access is denied. Doing the same replace through a tool that then trusts the result (Claude Code's Edit, with a handle held open on the target) produces the -vv log above: File.Rename failed in Cache … Access is denied followed by >Rename: errc=0, and the file's contents are unchanged on both the mount and the remote while the tool reports success.
Run the command 'rclone version' and share the full output of the command.
rclone v1.74.1
- os/version: Microsoft Windows 11 Pro 25H2 25H2 (64 bit)
- os/kernel: 10.0.26200.8655 (x86_64)
- os/type: windows
- os/arch: amd64
- go/version: go1.26.3
- go/linking: static
- go/tags: cmount
Which cloud storage system are you using? (eg Google Drive)
SFTP backend (to a Linux host), mounted via WinFsp.
The command you were trying to run (eg rclone copy /tmp remote:tmp)
rclone mount myremote:/path R: --vfs-cache-mode writes --vfs-write-back 1s --volname rclone-repro --dir-cache-time 2s --attr-timeout 2s -vv
The rclone config contents with secrets removed.
[myremote]
type = sftp
host = <redacted-host>
user = <redacted-user>
key_file = C:\Users\<user>\.ssh\id_ed25519
shell_type = unix
A log from the command with the -vv flag
The decisive excerpt is quoted above (`File.Rename failed in Cache … Access is denied` → `>Rename: errc=0` → `OpenEx: errc=-5`). Happy to attach the full `-vv` capture.
Suggested fix directions
1. Don't return success on a failed cache rename. At minimum, `item.rename()` should propagate the error (surface `EACCES`/`EIO`) instead of the `>Rename: errc=0` above, so callers don't silently lose data.
2. Coordinate before the cache rename (preferred): cancel/await the in-flight write-back upload and close `item.fd` before the local `os.Rename`, then re-queue.
3. Windows POSIX-semantics rename: `SetFileInformationByHandle` + `FileRenameInfoEx` with `FILE_RENAME_FLAG_POSIX_SEMANTICS | REPLACE_IF_EXISTS`, which can replace a file that has open handles but needs the handles opened with `FILE_SHARE_DELETE`.
- Retry-with-backoff on the local rename (simplest, cross-platform-safe, but adds latency).
Happy to attempt a PR (leaning toward #1 + #2) if that's welcome.
Thanks so much for considering this report, and more importantly, for making great software that's essential in my and many others' daily lives!