Renaming a synced file into an excluded name strands the replacement forever — silent data loss, UI shows "up to date"
Summary
On the macOS File Provider client, if an application saves a file by renaming the
old version to a name that matches an entry in sync-exclude.lst and then writing
a new file under the original name — the standard "keep one backup" save pattern
used by Blender, and by many other applications — then:
- the rename to the excluded name is rejected with
NSFileProviderErrorDomain -2005
(cannotSynchronize) rather than being handled as "this item has left the
synced set",
- macOS marks that operation
permanentThrottling / next: never — it will
never be retried,
- because the rename never lands, the original filename stays occupied on the
server by the stale item, so the newly written file cannot be created — it sits
at ⧗pathMatching with no server counterpart at all, indefinitely,
- the server keeps serving the second-newest version of the file forever, and
- the client UI reports everything as fully synced.
Every subsequent save repeats the cycle, so the divergence never heals and never
surfaces. The user's working file exists on exactly one disk.
This is not "a few files are behind". The affected files are permanently pinned to
the last version that synced before the exclude rule first matched, and nothing in
the client says so.
Environment
|
|
| Client |
Nextcloud Desktop 34.0.1 (build 34.0.1.0), File Provider / VFS |
| macOS |
15.7.4 (24G517) |
| Exclude rule |
*.blend1, added to the user sync-exclude.lst |
| Application |
Blender (saves foo.blend by moving it to foo.blend1, then writing a new foo.blend) |
User exclude list — ~/Library/Containers/com.nextcloud.desktopclient/Data/Library/Preferences/Nextcloud/sync-exclude.lst:
]~$*
.htaccess
*.blend1 <-- the only relevant addition over the bundled list
Steps to reproduce
- Add
*.blend1 to sync-exclude.lst and restart the client.
- Put
foo.blend in the sync folder. Let it upload. Confirm it is on the server.
- Open it in Blender and save. Blender renames
foo.blend → foo.blend1 and writes
a new foo.blend.
- Wait. The client shows no error and reports the folder as synced.
- Download
foo.blend from the server web UI and compare it to the local foo.blend.
Expected: the server has the file you just saved.
Actual: the server's foo.blend is byte-for-byte the previous version — it is
identical to the local foo.blend1. The version you saved is on the Mac only, and
will stay that way through every future save.
Verification on a live install
Head-21P-Classic28-v2.blend, saved in Blender on 22 Aug 11:38, downloaded from the
Nextcloud web UI on 23 Aug:
6900123 bytes sha256 5f8e3a3b40acea2b… ~/Downloads/Head-21P-Classic28-v2.blend <- from server
6910454 bytes sha256 7e3e1f3c91b5879e… …/21P/HEAD/Head-21P-Classic28-v2.blend <- local, current
DIFFERENT — size delta +10331 bytes, first differing byte at offset 32352
and the server's copy against the local backup file:
6900123 bytes sha256 5f8e3a3b40acea2b… ~/Downloads/Head-21P-Classic28-v2.blend <- from server
6900123 bytes sha256 5f8e3a3b40acea2b… …/21P/HEAD/Head-21P-Classic28-v2.blend1 <- local backup
IDENTICAL
So the server is serving what Blender considers the backup of the previous save.
Root cause, from fileproviderctl dump
Two records for the same logical file. Names are elided by fileproviderctl;
H{19}2.blend1 is Head-21P-Classic28-v2.blend1, resolved by size.
The old item — server identity of …-v2.blend, now named .blend1 locally:
<s:docID(214739) p:fileID(71589552) n:"H{19}2.blend1" doc sz:6900123
m:rw-%<70939778:SU:createdLocally> …>
docID(214739) fields:filename|structure content:watch
sver:fileID(71589552)/H{19}2.blend cver:72357176@7:sz:6900123
⧗throttling|⧗permanentThrottling>
<-> <fp:✅ 00058984oc3ac1etlfsm …>
Note sver: — the server-side name is .blend, while the local name is now
.blend1. The pending operation syncs fields:filename|structure: it is the
rename, not a content upload. Its state is permanentThrottling.
The new item — the file the user actually saved:
<s:docID(214741) p:fileID(71589552) n:"H{19}2.blend" doc sz:6910454
m:rw- …>
docID(214741) fields:filename|parentID|…|typeAndCreator content:watch ⧗pathMatching>
<-> <fp:✅ <unknown>
<fp:✅ <unknown> — no server counterpart exists. It is blocked at
⧗pathMatching because its name is still held on the server by docID(214739).
The operation that fails, and how:
i:docID(214739) update-item: 🛑 last:'1787369946 (-21h40min)' next:'never' count:1
error:'NSError: FP -2005 "The operation couldn't be completed.
(NSFileProviderErrorDomain error -2005.)" ' domain:cannotSynchronize
One attempt. next: never. Permanent.
The core defect
The same exclude rule produces two different errors from the extension, in the
same domain, minutes apart:
i:docID(214737) create-item: 🔶 … error:'NSError: FP -2010 …' domain:excludedFromSync <- correct
i:docID(214739) update-item: 🛑 … next:'never' error:'NSError: FP -2005 …' domain:cannotSynchronize <- wrong
create-item on an excluded name correctly returns -2010 excludedFromSync,
which macOS treats as benign and non-retryable — the item is simply out of scope.
update-item that renames an existing synced item into an excluded name returns
-2005 cannotSynchronize — a generic "the server refused this" — which macOS
treats as a hard failure and permanently throttles.
A rename out of the synced set is not a failure. It means the item has left the
synced set, and the correct propagation is to delete it server-side (as the
legacy non-VFS client does), which frees the filename so the replacement file can
be created. Rejecting the rename instead leaves the stale item squatting on the
real filename forever and orphans the new one.
Blast radius on this install
Six files, all following the same pattern, stuck between 17 and 85 hours, all with
next: never. Every one of them is a .blend whose current contents exist only on
this Mac:
Server-side name (sver) |
Local name now |
Stuck for |
21P/HEAD/…Classic28-v2.blend |
.blend1 |
21h40m |
21P/HEAD/…Classic25-v2.blend |
.blend1 |
22h07m |
21P/HEAD/…Classic25-v1.blend |
.blend1 |
45h01m |
21P/HEAD/…Round16-v1.blend |
.blend1 |
45h08m |
21P/BACK/…Round30-v1.blend |
.blend1 |
45h12m |
26P/HEAD/…Classic26-v1.0.blend |
.blend1 |
16h55m |
All four .blend files in 21P/HEAD show <fp:✅ <unknown> — none of their current
contents exist on the server.
Throughout, the client UI showed the account as synced with no errors.
Why this is worse than an ordinary sync failure
- It is silent. There is no error, no badge, no entry in the activity list.
- It is permanent.
next: never means no retry, ever, without intervention.
- It looks like success. The file is present on the server, at the right path,
with a plausible size and a recent-ish mtime. Only a byte comparison reveals that
it is a generation behind.
- It compounds. Each save strands another version; the gap between server and
disk widens silently and indefinitely.
- The excluded file is a backup file, so the natural assumption — "the backup
doesn't sync, fine" — is exactly the assumption that hides the loss of the
primary file.
Anyone who excludes an editor's backup-file pattern (*.blend1, *~, *.bak) is
exposed. Note that the bundled list already ships *~, which is precisely the
backup-file convention of a large family of Unix editors.
Suggested fix
Treat a rename whose destination matches the exclude list as the item leaving the
synced set:
- propagate it as a delete of the server-side item (freeing the name), and
- return -2010
excludedFromSync rather than -2005 for any excluded-path
rejection, so macOS stops permanently throttling an item it could otherwise
reconcile.
At minimum, -2005 … next:'never' on a user-initiated change should be surfaced in
the client UI. An error the OS has permanently given up on should never be invisible.
Renaming a synced file into an excluded name strands the replacement forever — silent data loss, UI shows "up to date"
Summary
On the macOS File Provider client, if an application saves a file by renaming the
old version to a name that matches an entry in
sync-exclude.lstand then writinga new file under the original name — the standard "keep one backup" save pattern
used by Blender, and by many other applications — then:
NSFileProviderErrorDomain -2005(
cannotSynchronize) rather than being handled as "this item has left thesynced set",
permanentThrottling/next: never— it willnever be retried,
server by the stale item, so the newly written file cannot be created — it sits
at
⧗pathMatchingwith no server counterpart at all, indefinitely,Every subsequent save repeats the cycle, so the divergence never heals and never
surfaces. The user's working file exists on exactly one disk.
This is not "a few files are behind". The affected files are permanently pinned to
the last version that synced before the exclude rule first matched, and nothing in
the client says so.
Environment
*.blend1, added to the usersync-exclude.lstfoo.blendby moving it tofoo.blend1, then writing a newfoo.blend)User exclude list —
~/Library/Containers/com.nextcloud.desktopclient/Data/Library/Preferences/Nextcloud/sync-exclude.lst:Steps to reproduce
*.blend1tosync-exclude.lstand restart the client.foo.blendin the sync folder. Let it upload. Confirm it is on the server.foo.blend→foo.blend1and writesa new
foo.blend.foo.blendfrom the server web UI and compare it to the localfoo.blend.Expected: the server has the file you just saved.
Actual: the server's
foo.blendis byte-for-byte the previous version — it isidentical to the local
foo.blend1. The version you saved is on the Mac only, andwill stay that way through every future save.
Verification on a live install
Head-21P-Classic28-v2.blend, saved in Blender on 22 Aug 11:38, downloaded from theNextcloud web UI on 23 Aug:
and the server's copy against the local backup file:
So the server is serving what Blender considers the backup of the previous save.
Root cause, from
fileproviderctl dumpTwo records for the same logical file. Names are elided by
fileproviderctl;H{19}2.blend1isHead-21P-Classic28-v2.blend1, resolved by size.The old item — server identity of
…-v2.blend, now named.blend1locally:Note
sver:— the server-side name is.blend, while the local name is now.blend1. The pending operation syncsfields:filename|structure: it is therename, not a content upload. Its state is
permanentThrottling.The new item — the file the user actually saved:
<fp:✅ <unknown>— no server counterpart exists. It is blocked at⧗pathMatchingbecause its name is still held on the server by docID(214739).The operation that fails, and how:
One attempt.
next: never. Permanent.The core defect
The same exclude rule produces two different errors from the extension, in the
same domain, minutes apart:
create-itemon an excluded name correctly returns -2010excludedFromSync,which macOS treats as benign and non-retryable — the item is simply out of scope.
update-itemthat renames an existing synced item into an excluded name returns-2005
cannotSynchronize— a generic "the server refused this" — which macOStreats as a hard failure and permanently throttles.
A rename out of the synced set is not a failure. It means the item has left the
synced set, and the correct propagation is to delete it server-side (as the
legacy non-VFS client does), which frees the filename so the replacement file can
be created. Rejecting the rename instead leaves the stale item squatting on the
real filename forever and orphans the new one.
Blast radius on this install
Six files, all following the same pattern, stuck between 17 and 85 hours, all with
next: never. Every one of them is a.blendwhose current contents exist only onthis Mac:
sver)21P/HEAD/…Classic28-v2.blend.blend121P/HEAD/…Classic25-v2.blend.blend121P/HEAD/…Classic25-v1.blend.blend121P/HEAD/…Round16-v1.blend.blend121P/BACK/…Round30-v1.blend.blend126P/HEAD/…Classic26-v1.0.blend.blend1All four
.blendfiles in21P/HEADshow<fp:✅ <unknown>— none of their currentcontents exist on the server.
Throughout, the client UI showed the account as synced with no errors.
Why this is worse than an ordinary sync failure
next: nevermeans no retry, ever, without intervention.with a plausible size and a recent-ish mtime. Only a byte comparison reveals that
it is a generation behind.
disk widens silently and indefinitely.
doesn't sync, fine" — is exactly the assumption that hides the loss of the
primary file.
Anyone who excludes an editor's backup-file pattern (
*.blend1,*~,*.bak) isexposed. Note that the bundled list already ships
*~, which is precisely thebackup-file convention of a large family of Unix editors.
Suggested fix
Treat a rename whose destination matches the exclude list as the item leaving the
synced set:
excludedFromSyncrather than -2005 for any excluded-pathrejection, so macOS stops permanently throttling an item it could otherwise
reconcile.
At minimum,
-2005 … next:'never'on a user-initiated change should be surfaced inthe client UI. An error the OS has permanently given up on should never be invisible.