[stable-34.0] Detect upload conflicts with an If-Match precondition#10394
Merged
Conversation
…tion
Content uploads on the modify path were unconditional PUTs: the extension
never told the server "only accept this if your copy is still the version I
edited." When the server copy changed since the last sync — another device or
user, or an app such as Adobe InDesign/Illustrator issuing rapid multi-step
re-saves — the client silently overwrote it (last-writer-wins, data loss).
Send the base version's etag as an `If-Match` precondition so the server
rejects a conflicting write with 412 Precondition Failed instead of clobbering.
`modifyContents` now takes `baseVersion` + `options` (threaded from the
`modify` call site) and derives the base etag from `baseVersion.contentVersion`
(falling back to the stored metadata etag).
Behaviour is tiered by OS, because Apple's real conflict-resolution contract —
`NSFileProviderModifyItemFailOnConflict` and the
`.localVersionConflictingWithServer` error — is macOS 26.0+ only, while the
extension deploys back to macOS 13:
* macOS 26+ with the system's `.failOnConflict` option: on 412 return
`.localVersionConflictingWithServer` so the system creates a conflict copy
and both versions survive. Advertised via
`NSExtensionFileProviderSupportsFailingUploadOnConflict` in Info.plist.
* macOS 13–25 (best-effort heuristic): always send `If-Match`; on 412 clear
any lock token, signal a working-set re-enumeration, and return a transient
NSCocoaError so the system re-fetches the server version before re-driving
the edit — turning a silent overwrite into a visible sync round-trip.
* macOS 26+ without `.failOnConflict`: unchanged (no `If-Match` sent).
A `sentIfMatch` guard (we only send `If-Match` when a base etag exists) gates
the new 412-as-conflict branch, so a lock-only 412 keeps its previous
stale-lock meaning.
Scope: the single-PUT modify path. Chunked (large-file) uploads need an
upstream NextcloudKit change (an `If-Match` scoped to the assembly MOVE only,
so it does not ride on the chunk PUTs) and are a deferred follow-up.
Tests: added coverage for the precondition being sent, the macOS 26+ conflict
return, and the macOS < 26 transient-retry path; updated the existing 412
lock-token test for the new OS-dependent behaviour; added a
`lastUploadIfMatchHeader` capture knob to the mock remote interface.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
backportbot
Bot
requested review from
claucambra,
mgallien and
nilsding
as code owners
July 17, 2026 14:07
claucambra
approved these changes
Jul 17, 2026
Contributor
|
Artifact containing the AppImage: nextcloud-appimage-pr-10394.zip Digest: To test this change/fix you can download the above artifact file, unzip it, and run it. Please make sure to quit your existing Nextcloud app and backup your data. |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Backport of PR #10393