Skip to content

One unreadable folder silently stalls ALL remote→local sync (working-set scan aborts on first read error) #10442

Description

@i2h3

On the macOS File Provider client, remote changes (files created/renamed on the server) stop appearing locally, with no error shown to the user. Restarting the client does not help. Local→remote uploads may still work, so it looks like a partial/one-way stall.

Root cause: the working-set remote-change scan aborts the entire pass on the first non-404 read error. Because a change notification on macOS only ever signals .workingSet, scanMaterialisedItemsForRemoteChanges() is the single code path that pulls server→client changes into the framework. It walks the materialised items sorted parent-first (shortest remote path first — so the account root and top-level folders are read first) and does:

// Enumeration/Enumerator+WorkingSetScan.swift
} else if let readError = readResult.error, readError != .success {
    logger.error("Finished remote change enumeration of materialised items with error.", [.error: readError])
    break   // <-- abandons the rest of the queue
}

If an early folder's depth-1 PROPFIND fails (e.g. a very large container that times out, or a transient 5xx), the break abandons every remaining folder, so no remote change for any folder is discovered. Worse, the failure is swallowed: the scan returns its partial (often empty) result with no error, and enumerateWorkingSetChanges then reports finishEnumeratingChanges(upTo: currentAnchor, moreComing: false) with a fresh "now" anchor — telling the framework the domain is fully synced. No error surfaces, and the sync point advances past changes that were never discovered.

This is intermittent and strongly correlated with large/slow accounts: the failing reads are the biggest containers (root, and folders with very many children). On a healthy account an occasional failure is masked by the next successful pass; on a large account nearly every pass fails at the same early folder, so it never catches up.

Steps to reproduce

  1. Use an account large enough that a top-level folder's unpaginated depth-1 PROPFIND intermittently fails/times out (or inject a transient non-404 error on an early folder read).
  2. On the web UI, add or rename a file in a folder that is materialised/known locally.
  3. Wait for a working-set sync cycle.

Expected: the new/renamed file appears locally within a cycle.
Actual: it never appears; no error is shown; restarting the client does not help.

Symptoms observed (from debug archives)

  • Read of URL did fail and Finished remote change enumeration of materialised items with error recur continuously; the failing URLs are the account root and the largest folders.
  • The extension is not hung — working-set change-observation cycles complete every few minutes, each discovering nothing.
  • Reproduces immediately after a clean restart (it is a server/account-state condition, persisted via the materialised set in the Realm DB).
  • Local→remote uploads/deletes succeed in the same window (the abort only severs server→client).

Contributing issue — diagnostics blind spot

Every failure logs as an opaque NKError code 1 / domain NextcloudKit.NKError. NKError is a Swift struct with no CustomNSError conformance, so FileProviderLogDetail bridges it via as? NSError and logs the default code 1, discarding the real errorCode/errorDescription (Log/FileProviderLogDetail.swift). This hid the true failure (timeout vs 5xx vs XML-decode) and should be fixed so these are diagnosable.

Contributing issue — trashbin permanent-delete retry loop

Permanent deletion of a trashed item issues DELETE to …/dav/trashbin/<user>/trash/<name> using the stored "rough" plain filename (Item+Delete.swift handleMetadataTrashModification), but the server names collided trash entries <name>.d<deletion-timestamp>. The plain path 404s forever and macOS retries with no backoff — a chronic loop (thousands of failures against a small fixed set of items) that adds constant signalEnumerator churn and server load.

Proposed fix

  1. Working-set scan: replace the break with continue — skip only the unreadable folder and keep scanning the rest of the working set; track that a read failed.
  2. Anchor honesty: when the scan was incomplete, do not advance the working-set sync anchor to a fresh "now" (keep the incoming anchor) so the next signal re-derives and converges instead of silently reporting "synced".
  3. (Trigger) paginate the working-set/change reads (as on-demand enumerateItems already does) and/or raise the request timeout for large containers, so big-folder PROPFINDs stop failing.
  4. Logging: special-case NKError in FileProviderLogDetail to log its real errorCode/errorDescription.
  5. Trashbin: resolve the real .d<timestamp> trashbin name from a fresh trash listing before a permanent delete, and treat an item already absent from the trash as successfully deleted (stop the retry loop).

A regression test (RemoteChangePropagationTests) that injects a non-404 error on the first-scanned folder and asserts a later folder's change still surfaces reproduces the bug and passes after fix #1#2.

Environment

  • Nextcloud Desktop Client 33.0.7 (macOS, File Provider extension)
  • macOS (File Provider / VFS mode)
  • Server: recent stable

Metadata

Metadata

Assignees

Labels

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

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions