Skip to content

Silent server-side DELETE when path contains a diacritic stored on the server in NFD form #10087

@fokklz

Description

@fokklz

⚠️ Before submitting, please verify the following: ⚠️

Bug description

Authorship & verification disclaimer. This report was drafted by an AI assistant (Anthropic Claude) working from a customer debug-bundle, with iterative human direction and verification. I — Fokklz — reviewed each claim, ran the suggested commands, and pushed back where things didn't add up; however, my own background in Unicode normalisation, Swift String semantics, and Realm-core internals isn't deep enough for me to independently audit every assertion to the byte level. Treat this as a well-evidenced tip rather than a fully self-audited bug report. I have the complete customer debug bundles, the per-device forensic markdown reports, and the agent-by-agent verification artefacts (including the Swift execution output, the realm-swift → realm-core source trace, the Docker reproduction transcripts, and the JSONL byte-level verification) and am happy to share them privately as a Git repository on request — please reach out on Discord (@Fokklz) or by email (chat@fokklz.dev). I'd rather not publish the raw bundles publicly because they contain server URLs and account identifiers that are obfuscated in this issue but present verbatim in the logs.

TL;DR

On macOS, the file-provider extension's Realm cache is queried with a byte-level == predicate that does not Unicode-normalise. When NSFileProviderManager hands the FPE an NFD-form URL for a path the server stores in NFC (or vice versa), the Realm lookup at FilesDatabaseManager.swift:179 misses, the FPE treats the item as new/conflicted, and the downstream conflict-resolution path issues a server-side DELETE. The main desktop log shows zero entries at Qt severity error|critical|fatal. The C++ sync engine that runs on Windows/Linux ships QString::NormalizationForm_C defences at six boundaries; the Swift FPE that runs on macOS-in-FPE-mode (the default and only supported mode on modern macOS) ships zero.

Affected versions

  • Nextcloud Desktop: 33.0.2 (macOS, cocoa), git revision 54f537e35f8bea165d396bdfb55ab0c02408755c, Qt 6.10.2.
  • NextcloudKit: 7.2.6 (pinned by Package.resolved).
  • macOS: reproduced on Tahoe 26.2 and 26.4; also seen on Sequoia 15.
  • Server: any (the bug is in the client). Customer was on Nextcloud Enterprise 32.0.6.1.
  • Auth: webflow / Microsoft-federated OIDC; reproduced on 4 distinct accounts on the same server.

Severity

Critical — silent data loss. No user-facing surfacing of the deletion.

Confirmed deletions (anonymised)

Files DELETEd from the server, confirmed via the FPE's JSONL log. Both rows below were byte-verified — the URL fields in the JSONL alternated between NFD (pre\xcc\x82t) and NFC (pr\xc3\xaat) enumerator events in the minute before the DELETE fired.

Account Deleted item DELETE timestamp (CEST) Confirmed NFD/NFC alternation in logs?
A …/MAMCO/AMRLEDER_KBCB_fiche_prêt_1sign_SignPB[47].pdf 2026-04-20 10:20:54 yes — 3 NFD events (pre\xcc\x82t), 11 NFC events (pr\xc3\xaat) for the same ocId 02208123oczue8n54sn9 within 70 s, then DELETE
A …/MAMCO/AMRLEDER_KBCB_fiche_prêt_1sign_SignPB[75].pdf 2026-04-20 10:20:50 partial — captured logs show NFC enumerator events; NFD form present in upload-time message body
A …/Constat MAMCO/Arrival/CR_Pasquart_Muster.docx 2026-04-20 10:15:56 no — pure ASCII filename. Listed here because it was also DELETEd in the same window, but it is NOT evidence for the NFC/NFD mechanism.
B …/AMRLEDER_KBCB_fiche_prêt_1sign_SignPB.pdf 2026-04-21 09:18:06 yes — 2 NFD events, ≥6 NFC events for ocId 02209281oczue8n54sn9 within 51 s, then DELETE

Verbatim log evidence — byte-level

Bundle selma_pc/all_synced_but_missing_files/File Provider Domains/<UUID-A>/Logs/2026-04-20_09-05-06 (1332).jsonl. The relevant lines (JSONL line numbers, timestamps verbatim, URL byte sequences verified via Python repr().encode()):

Line 650 at 2026.04.20 10:19:44.436Set up enumerator. for ocId 02208123oczue8n54sn9, URL bytes contain pre\xcc\x82t (NFD: p r e U+0302 t).

Line 664 at 2026.04.20 10:19:58.328 (14 s later) — Set up enumerator. for the same ocId, URL bytes now contain pr\xc3\xaat (NFC: p r U+00EA t).

Lines 665, 683, 689, 692, 693, 716, 717 — further enumerator setups, all NFC, over the next 70 s.

Line 720 at 2026.04.20 10:20:54.448:

{"category":"Item","date":"2026.04.20 10:20:54.448","details":{"item":"02208123oczue8n54sn9","url":"…/SignPB[47].pdf"},"level":"info","message":"Successfully deleted item."}

Line 724 at 2026.04.20 10:20:55.442Read of URL did fail. with NextcloudKit.NKError code 1 for the same path (the file is no longer on the server; PROPFIND 404s).

Same pattern (NFD enumerator events at upload time → NFC enumerator events → single DELETE) confirmed for SignPB.pdf in bundle collection_pc/no_sync_pre_reset/File Provider Domains/<UUID-B>/Logs/2026-04-21_09-04-07 (1234).jsonl.

Throughout both incidents the main desktop log (Nextcloud.log.0) shows zero entries at Qt severity error|critical|fatal:

$ grep -oE '\[ (debug|info|warning|critical|error|fatal) ' nextcloud.log.0 | sort | uniq -c
  19865 [ info
    192 [ warning
     22 [ debug

(Detailed byte-level forensic report at _VERIFICATION_jsonl.md in the source bundle.)

Steps to reproduce

Reproducer (best-effort from observed evidence)

  1. macOS 14+/15/26, Nextcloud Desktop 33.x in file-provider mode (default on macOS).
  2. Server contains at least one file at a path whose name has an NFD-encoded diacritic. Engineer it by uploading via a Mac shell:
    curl -u user:pass -T file.pdf "https://server/remote.php/dav/files/user/$(printf 'pre\xcc\x82t.pdf')"
  3. Mount the account in Finder; navigate to the parent directory so the FPE enumerates it.
  4. Trigger one or more re-enumerations (view-mode switch in Finder, thumbnail generation, Spotlight).
  5. Watch ~/Library/Group Containers/<TEAM_ID>.com.nextcloud.desktopclient/File Provider Domains/<UUID>/Logs/<date>.jsonl for Successfully deleted item. followed by PROPFIND 404s on the original URL.

(Source of the path: FileManager+FileProviderDomainLogDirectory.swift:23-26 + FileManager+applicationGroupContainer.swift:25.)

Expected behavior

Code archaeology — the defence exists but does not apply on macOS-in-FPE-mode

Important framing: the 33.x macOS client ships two parallel implementations. The C++ sync engine under src/ is what runs on Windows, on Linux, and on macOS in legacy (non-file-provider) mode. The Swift File Provider Extension under shell_integration/MacOSX/ is what runs on macOS in file-provider mode — the default and only supported mode on modern macOS. The C++ engine has Unicode-normalisation defences and always has; the Swift extension does not. On a macOS 33.x install with the file provider active, the C++ defences are dead code; the Swift extension owns every byte the server sees.

The C++ tree (verbatim in v4.0.5 and still present in v33.0.2 — these are NOT what runs on macOS-in-FPE-mode):

File:line Boundary normalised
src/libsync/owncloudpropagator.cpp:813 case-clash detection. Comment: // Need to normalize to composited form because of QTBUG-39622/QTBUG-55896
src/common/syncjournaldb.cpp:1007 every name written to the sync journal DB on macOS
src/gui/folderwatcher_mac.cpp:79 every macOS FSEvents path
src/gui/folder.cpp:222 sync folder canonical path (comment: // Workaround QTBUG-55896)
src/gui/socketapi/socketapi.cpp:376 every line from the socket API
src/gui/macOS/fileprovidersocketcontroller.cpp:58 every line from the legacy file-provider socket controller

The Swift File Provider tree:

$ grep -rEn 'precomposed|decomposed|NFC|NFD|canonicalMapping|Normalization' \
       shell_integration/MacOSX/NextcloudFileProviderKit/ \
       shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/ \
       --include='*.swift'
(no matches)

(The Objective-C FinderSync.m does call decomposedStringWithCanonicalMapping, but that's a separate overlay-badge subsystem and uses NFD on purpose because Finder hands it NFD paths. It does NOT touch sync data.)

Which files are affected by this bug

…/MAMCO/AMRLEDER_KBCB_fiche_prêt_1sign_SignPB[47].pdf

Operating system

macOS

Which version of the operating system you are running.

Tahoe 26.2 and 26.4

Installation method

Other

Nextcloud Server version

Nextcloud Enterprise 32.0.6.1

Nextcloud Desktop Client version

33.0.2 (macOS, cocoa), git revision 54f537e35f8bea165d396bdfb55ab0c02408755c, Qt 6.10.2.

Did this occur after an update or on a clean installation?

Major version update (i.e. 4.0.0 → 33.0.0)

Are you using the Nextcloud Server Encryption module?

No

Are you using an external user-backend?

  • Default internal user-backend
  • LDAP or Active Directory
  • SSO - SAML
  • Other

Nextcloud Server logs

The affected server is a Hetzner-managed Nextcloud Enterprise tenant; I do not have access to its server-side logs (PHP error log, Apache/nginx access log, audit log, `oc_files_*` table dumps). All evidence in this report is from the macOS client side: the file-provider extension's JSONL logs, the main desktop log, the Realm cache files, the local sync-folder state, and static analysis of the published client + server source.

Additional info

Latent NFD-in-Realm on a healthy device

kvm_pc (a customer device that's working fine) has a Realm whose strings dump contains both NFC and NFD spellings of könnten in the same folder name (25.04. Wenn Stoffe sprechen könnten). This shows that NFD strings can enter the Realm even without an overt failure; the conflict-resolution path doesn't fire on this device — yet.

Open questions (not yet answered by available evidence)

  1. The exact downstream code path that turns a Realm lookup miss into a server DELETE. The JSONL log confirms (a) repeated NFC/NFD enumerator alternation, (b) eventual Successfully deleted item. from Item+Delete.swift, but the decision to delete is made somewhere between the enumerator and Item+Delete.swift and a maintainer with FPE source access can trace it directly.
  2. The CR_Pasquart_Muster.docx deletion (pure ASCII, no NFC/NFD ambiguity) which occurred in the same minute and same account — listed in the deletions table for transparency, but cannot be attributed to NFC/NFD. It may share a different trigger; suggests there's a secondary issue not characterised by this report.

Cross-links

Metadata

Metadata

Assignees

Labels

1. to developbugfeature: 📁 file providermacOS File Provider Extension, more general also known as virtual file system.os: 🍎 macOSApple macOS, formerly also known as OS X

Type

No fields configured for Bug.

Projects

Status

📄 To do

Relationships

None yet

Development

No branches or pull requests

Issue actions