Bisync --max-delete behavior conflicts with --track-renames during directory renames

What is the problem you are having with rclone?

rclone bisync currently evaluates --max-delete before --track-renames is executed. This leads to false aborts when entire directories are renamed, even though no actual deletions will occur.

Steps to reproduce:

Given both /path1 and /path2 contain this tree (previously synced via --resync):

/path1
├── folder1
│   ├── file1
│   ├── file2
│   ├── file3
│   ├── file4
│   ├── file5
│   ├── file6
│   ├── file7
│   ├── file8
│   └── file9
└── folder2
    └── file10

Then, rename folder1 locally:

mv /path1/folder1 /path1/folder1_renamed

Now run:

rclone bisync /path1 /path2 --track-renames --max-delete 20

Expected:

folder1 on /path2 is renamed to folder1_renamed, preserving all contents.

Actual:

Safety abort: too many deletes (>20%, 9 of 10) on Path1 "/path1/". Run with --force if desired.

If I retry with --force, the run completes successfully — no deletions occur, only renames. But using --force defeats the safety purpose of --max-delete.

In this post ncw explains that:

But the --max-delete check is done before this rename resolution. So it assumes that all unmatched destination files (e.g., folder1/*) will be deleted, triggering a false-positive safety abort.

Suggestion:

The --max-delete logic should be applied after --track-renames processing — i.e., only count actual unmatched entries in dstFiles that would be deleted.

Run the command 'rclone version' and share the full output of the command.

rclone version
rclone v1.70.3

  • os/version: debian 12.11 (64 bit)
  • os/kernel: 6.1.31-sun50iw9 (aarch64)
  • os/type: linux
  • os/arch: arm64 (ARMv8 compatible)
  • go/version: go1.24.4
  • go/linking: static
  • go/tags: none

Which cloud storage system are you using? (eg Google Drive)

None, test run local

The command you were trying to run (eg rclone copy /tmp remote:tmp)

rclone bisync /path1 /path2 --track-renames --max-delete 20

The rclone config contents with secrets removed.

rclone config
Current remotes:

Name                 Type
====                 ====
GoogleDrive          drive

e) Edit existing remote
n) New remote
d) Delete remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config
e/n/d/r/c/s/q>

A log from the command with the -vv flag

rclone bisync /path1 /path2 --track-renames --max-delete 20 -vv
2025/07/13 14:38:09 DEBUG : rclone: Version "v1.70.3" starting with parameters ["rclone" "bisync" "/path1" "/path2" "--track-renames" "--max-delete" "20" "-vv"]
2025/07/13 14:38:09 DEBUG : Creating backend with remote "/path1"
2025/07/13 14:38:09 DEBUG : Using config file from "/root/.config/rclone/rclone.conf"
2025/07/13 14:38:09 DEBUG : Creating backend with remote "/path2"
2025/07/13 14:38:09 NOTICE: bisync is IN BETA. Don't use in production!
2025/07/13 14:38:09 INFO  : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set.
2025/07/13 14:38:09 INFO  : Bisyncing with Comparison Settings:
{
        "Modtime": true,
        "Size": true,
        "Checksum": false,
        "HashType1": 0,
        "HashType2": 0,
        "NoSlowHash": false,
        "SlowHashSyncOnly": false,
        "SlowHashDetected": true,
        "DownloadHash": false
}
2025/07/13 14:38:09 DEBUG : Lock file created: /root/.cache/rclone/bisync/path1..path2.lck
2025/07/13 14:38:09 INFO  : Synching Path1 "/path1/" with Path2 "/path2/"
2025/07/13 14:38:09 DEBUG : updated backup-dir for Path1
2025/07/13 14:38:09 DEBUG : updated backup-dir for Path2
2025/07/13 14:38:09 INFO  : Building Path1 and Path2 listings
2025/07/13 14:38:09 DEBUG : &{fs1:0x400084e000 fs2:0x400084e0a0 abort:false critical:false retryable:false basePath:/root/.cache/rclone/bisync/path1..path2 workDir:/root/.cache/rclone/bisync listing1:/root/.cache/rclone/bisync/path1..path2.path1.lst listing2:/root/.cache/rclone/bisync/path1..path2.path2.lst newListing1:/root/.cache/rclone/bisync/path1..path2.path1.lst-new newListing2:/root/.cache/rclone/bisync/path1..path2.path2.lst-new aliases:map[] opt:0x4000846120 octx:{emptyCtx:{}} fctx:{emptyCtx:{}} InGracefulShutdown:false CleanupCompleted:false SyncCI:<nil> CancelSync:<nil> DebugName: lockFile:/root/.cache/rclone/bisync/path1..path2.lck renames:map[] resyncIs1to2:false}: starting to march!
2025/07/13 14:38:09 DEBUG : folder1: path2 only
2025/07/13 14:38:09 DEBUG : folder1: is Dir
2025/07/13 14:38:09 DEBUG : folder1_renamed: path1 only
2025/07/13 14:38:09 DEBUG : folder1_renamed: is Dir
2025/07/13 14:38:09 DEBUG : folder2: both path1 and path2
2025/07/13 14:38:09 DEBUG : folder2: is Dir
2025/07/13 14:38:09 DEBUG : folder2/file10: both path1 and path2
2025/07/13 14:38:09 DEBUG : folder2/file10: is Object
2025/07/13 14:38:09 DEBUG : folder1/file1: path2 only
2025/07/13 14:38:09 DEBUG : folder1/file1: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file1: path1 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file1: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file2: path1 only
2025/07/13 14:38:09 DEBUG : folder1/file2: path2 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file2: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file3: path1 only
2025/07/13 14:38:09 DEBUG : folder1/file2: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file3: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file4: path1 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file4: is Object
2025/07/13 14:38:09 DEBUG : folder1/file3: path2 only
2025/07/13 14:38:09 DEBUG : folder1/file3: is Object
2025/07/13 14:38:09 DEBUG : folder1/file4: path2 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file5: path1 only
2025/07/13 14:38:09 DEBUG : folder1/file4: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file5: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file6: path1 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file6: is Object
2025/07/13 14:38:09 DEBUG : folder1/file5: path2 only
2025/07/13 14:38:09 DEBUG : folder1/file5: is Object
2025/07/13 14:38:09 DEBUG : folder1/file6: path2 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file7: path1 only
2025/07/13 14:38:09 DEBUG : folder1/file6: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file7: is Object
2025/07/13 14:38:09 DEBUG : folder1/file7: path2 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file8: path1 only
2025/07/13 14:38:09 DEBUG : folder1/file7: is Object
2025/07/13 14:38:09 DEBUG : folder1/file8: path2 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file8: is Object
2025/07/13 14:38:09 DEBUG : folder1/file8: is Object
2025/07/13 14:38:09 DEBUG : folder1_renamed/file9: path1 only
2025/07/13 14:38:09 DEBUG : folder1_renamed/file9: is Object
2025/07/13 14:38:09 DEBUG : folder1/file9: path2 only
2025/07/13 14:38:09 DEBUG : folder1/file9: is Object
2025/07/13 14:38:09 DEBUG : &{fs1:0x400084e000 fs2:0x400084e0a0 abort:false critical:false retryable:false basePath:/root/.cache/rclone/bisync/path1..path2 workDir:/root/.cache/rclone/bisync listing1:/root/.cache/rclone/bisync/path1..path2.path1.lst listing2:/root/.cache/rclone/bisync/path1..path2.path2.lst newListing1:/root/.cache/rclone/bisync/path1..path2.path1.lst-new newListing2:/root/.cache/rclone/bisync/path1..path2.path2.lst-new aliases:map[] opt:0x4000846120 octx:{emptyCtx:{}} fctx:{emptyCtx:{}} InGracefulShutdown:false CleanupCompleted:false SyncCI:<nil> CancelSync:<nil> DebugName: lockFile:/root/.cache/rclone/bisync/path1..path2.lck renames:map[] resyncIs1to2:false}: march completed. err: <nil>
2025/07/13 14:38:09 INFO  : Path1 checking for diffs
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file1
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file2
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file3
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file4
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file5
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file6
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file7
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file8
2025/07/13 14:38:09 INFO  : - Path1    File was deleted          - folder1/file9
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:02:59.427353314 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file1
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file2
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file3
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file4
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file5
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file6
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file7
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file8
2025/07/13 14:38:09 INFO  : - Path1    File is new               - folder1_renamed/file9
2025/07/13 14:38:09 INFO  : Path1:   18 changes:    9 new,    0 modified,    9 deleted
2025/07/13 14:38:09 INFO  : Path2 checking for diffs
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:22.355915576 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:26.401741704 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:28.168392421 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:30.345427299 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:32.075293903 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:34.17534032 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:36.308182322 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:38.597560316 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:01:40.475888109 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 DEBUG : 2025-07-13 17:02:59.427353314 +0000 UTC: modification time the same (differ by 0s, within tolerance 1ns)
2025/07/13 14:38:09 ERROR : Safety abort: too many deletes (>20%, 9 of 10) on Path1 "/path1/". Run with --force if desired.
2025/07/13 14:38:09 DEBUG : Lock file removed: /root/.cache/rclone/bisync/path1..path2.lck
2025/07/13 14:38:09 NOTICE: Bisync aborted. Please try again.
2025/07/13 14:38:09 INFO  :
Transferred:              0 B / 0 B, -, 0 B/s, ETA -
Errors:                 1 (retrying may help)
Checks:                20 / 20, 100%, Listed 22
Elapsed time:         0.0s

2025/07/13 14:38:09 DEBUG : 7 go routines active
2025/07/13 14:38:09 NOTICE: Failed to bisync: too many deletes

You are correct about why this is happening. However, I'm not sure I would classify it as a bug. It is a documented aspect of how --max-delete works:

One way to trigger this limit is to rename a directory that contains more than half of your files. This will appear to bisync as a bunch of deleted files and a bunch of new files.

I think the logic is that renaming more than X% of your files (50% by default, 20% in your case) is an unusual enough event that there is reason to question whether it's really what the user intended before we go ahead and do it. If renaming 50% of files is not unusual for a particular user, they could change the percentage to a level they would consider unexpected (perhaps 90%) or disable this feature.

I do hear you about renames being less destructive than deletes (at least for backends capable of server-side move, which is not all of them).

Practically speaking, it would be difficult to implement your suggestion, because by the time --track-renames has processed, some of the files have already been transferred (i.e. ones that have the same name on both sides), and therefore, it would be too late to truly abort the sync. In the case of actual deletes, that would be problematic, as we'd have partially performed a sync we should never have started. Perhaps bisync could instead do its own renameMap analysis before starting the sync -- but this would have a considerable performance cost, and I'm not sure it's worth the tradeoff (but feel free to push back if you disagree!).

Hi @nielash, thanks for the detailed explanation.

You're absolutely right, this current behavior is documented, and the --max-delete flag is doing its job as designed.

However, I’d like to respectfully push back on whether this should be the final behavior when --track-renames is explicitly enabled — especially considering what the bisync documentation on renamed directories says:

Renamed directories

By default, renaming a folder on the Path1 side results in deleting all files on the Path2 side and then copying all files again from Path1 to Path2. Bisync sees this as all files in the old directory name as deleted and all files in the new directory name as new.

A recommended solution is to use --track-renames, which is now supported in bisync as of rclone v1.66.

This implies that the user should expect bisync to handle directory renames by simply renaming them instead of deleting and copying over. The core idea is that a user opting into --track-renames is explicitly signaling that renames are safe and desirable. Therefore, large apparent deletions that are later resolved by renames shouldn't trigger a safety abort. This wouldn't require removing --max-delete, but rather making its calculation account for rename resolutions first, then evaluating the true destructive impact.

About the performance cost for a renameMap analysis before starting the sync, maybe this could be opt-in (e.g. --max-delete-renames-aware). That way, users who want maximum safety can retain strict aborts, and others who frequently rename large folders can avoid false positives without disabling safety checks entirely via --force.

1 Like

I think that's a reasonable solution. Would you like to create a Feature Request issue on Github? And any chance you would be interested in working on it?

Hi @nielash, thanks for the suggestion.

I’ll open a GitHub feature request to help move this forward.

As for contributing code, I’m more of a hobbyist and not experienced enough to dive into a large codebase like rclone’s. I understand the logic, but implementing it is beyond my current skills. Still, happy to document the issue clearly for others who might pick it up.

Thanks again for the support!

1 Like