Repository navigation
fix(sync): isolate unreadable local items instead of stopping folder sync - #500
Conversation
…sync A Git working tree could fail the whole desktop folder sync when one item vanished, stayed locked, or was not a regular file during a check, for example a transient index.lock, a file held open by another process, an fsmonitor socket, or a .git/index rewritten between scan and content verification. The local scan now reports such items individually. Their subtrees are withheld from local, remote, and baseline planning, so absence is never treated as a deletion or restore, and each item is recorded as a skipped work item with a path-specific reason. Local content-verification failures leave the candidate unverified instead of aborting the run. Unreadable sync roots and symbolic links still stop the folder. Advances #489
Obiente previewNC Native · Obiente updates this comment as the preview changes. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba755b09d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
An interrupted-replacement backup that the recovery walk or scan cannot read may hold the only local original. It now stops the folder instead of being skipped, so its destination is never planned as a deletion. A folder delivered at the 64-level walk depth limit now stops the scan with an explicit nesting error instead of being dropped when only a selected descendant includes it. Leaf validation reads the listed file's attributes once after checking its parents, so a file that vanishes or loses access in that window is reported as unavailable, while replaced parents and symbolic links still stop the folder. Advances #489
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2fd02b143
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…le remote subtrees A non-regular item that replaces a folder included only as an ancestor of a selected path is now reported as unavailable, so its subtree is withheld instead of planned as a deletion. Remote scans no longer descend into subtrees whose local counterpart is unavailable, so a large or temporarily unlistable server folder there cannot abort the run. Content verification now isolates only a changed local generation through a distinct exception; replaced or linked parent folders stop the folder again. Owned recovery artifact naming moves to its own file. Advances #489
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83c3e5623a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| override fun visitFileFailed(file: Path, exc: IOException): FileVisitResult { | ||
| requireRecoverableWalkFailure(file, exc) | ||
| return FileVisitResult.CONTINUE |
There was a problem hiding this comment.
Preserve failures observed during preflight
If a previously synchronized file is listed during preflight but vanishes or becomes unreadable before its attributes can be read, walkFileTree invokes this callback and the failure is now discarded. When the file is still absent from the subsequent scan, no unavailable item is produced, so its baseline and remote copy are planned as a local deletion and Propagate can delete the server copy. Carry preflight failures into the final unavailable set, or abort this check, rather than forgetting evidence that the path's state was ambiguous.
AGENTS.md reference: AGENTS.md:L15-L18
Useful? React with 👍 / 👎.
The Linux folder-sync guide now keeps its 2026-08-30 review date for the existing workflows and gives the unreadable-item behavior its own dated subsection, following the Windows Cloud Files guide pattern, so the banner and content metadata no longer disagree. Advances #489
Outcome
Advances #489.
Desktop folder sync could fail an entire folder pair when one local item could not be read. Nothing treats
.git, dot-folders, hidden files or extensionless names such asCOMMIT_EDITMSGspecially. The failure came from one bad item stopping the whole run, which Git repositories trigger often:.git/index.lock,ORIG_HEAD, files held by an editor or antivirus) threw out of the local scan..git/fsmonitor--daemon.ipc, or a Windows junction or placeholder) failed the scan even when an ignore rule covered it..git/index) aborted the run.Changes:
FileSyncLocalAvailability.ktwith typed unavailable items (Vanished, Unreadable, Unsupported). Unavailable paths are withheld from both the local and server sides of planning and baselines are untouched, so a skipped item is never planned as a deletion, restore or upload. Skipped work items name each path, capped at 1,000 per run, and saved pair validation rejects reports whose reason does not match the path.DesktopLocalSyncScan(documents, unavailable). Non-regular files covered by an ignore rule are skipped silently. An unreadable sync root, unsafe parent folders and symbolic links still stop the folder, as before..name.nextcloud-native-backup-<uuid>) or anything inside one stops the folder with a restore-access message, in the recovery, preflight and scan walks, so the destination is never planned as deleted while its original is stranded. An included folder at the 64-level walk depth limit stops the folder with a "nested more than 64 folders deep" message instead of being silently dropped.excludingUnavailableFileSyncPaths), so a remote listing error or size bound inside a withheld subtree no longer aborts the run. Withholding before planning remains as a second layer.DesktopFileSyncLocalRecoveryNames.kt;DesktopFileSyncLocalTree.ktis 783 lines.DesktopFileSyncEngineSupport.kt; the engine size baseline is lowered from 884 to 876.website/content/guides/linux-folder-sync.mddescribes skipped items.For reference, the official desktop client does not exclude
.gitby default and syncs hidden files by default. This PR adds no default exclusions.Verification
bash tools/check-repository.shpasseschanges/unreleased/fragment records the changeRun on Windows with JDK 21:
:ui:desktopTestfor the new and updated sync test classes: pass.:ui:desktopTest: 3787 tests, 0 failures, 69 skipped. AndroidcompileDebugKotlinAndroidand:androidApp:testDebugUnitTest: pass.bash tools/check-kotlin-architecture.sh,node tools/changelog-fragments.mjs validate,git diff --check: pass.New tests (synthetic trees only):
FileSyncLocalAvailabilityTest: withholding and nested-report collapse, an unreadable.git/indexor.git/objectsnever deleted even with deletion propagation, baselines kept, report limit, forged reasons rejected.DesktopFileSyncLocalAvailabilityTest: hidden.git,COMMIT_EDITMSG,ORIG_HEAD, a vanishingindex.lock, a file held open by another process, a Windows byte-range lock, a socket reported or skipped when ignored, POSIX unreadable folder and root, and a file rewritten while staged.DesktopFileSyncGitRepositoryTest: runs the real engine against an in-memory WebDAV test server. A full repository uploads byte-identical and a second run makes 0 operations; a socket inside.gitis skipped while everything else syncs; a lockedindex.lockis skipped and syncs once released;COMMIT_EDITMSGrewritten during upload syncs on the next run; with deletion propagation, moving.git/refsinto an unreadable backup stops the run with no DELETE sent, and after access returns the next run makes 0 operations..git/refs/heads/mainselected, replacing.git/refswith a socket skips one item and sends no DELETE; an unlistable local.git/refswith a remote 503 for that folder completes with one skipped item.DesktopFileSyncContentSliceSafetyTestcovers a same-size rewrite left unverified and a replaced parent stopping the folder without any server request. Each of these failed against the previous behavior.:ui:desktopTest :androidApp:testDebugUnitTest: 3797 tests, 0 failures, 69 skipped.Not run: the website build and guide-content test (website dependencies were not installed; frontmatter tests passed), the POSIX branch of the listing-denial tests on Linux or macOS, the symbolic-link leaf test (needs symlink privilege on the test host), and any real server or manual desktop run.
Compatibility and risk
.gitand other VCS metadata by default (two-way syncing a live repository between devices can corrupt it), and whether symbolic links inside a synced folder should be skipped per item instead of stopping the folder.Visual changes
Not applicable. The only visible change is the skipped-item count in the sync run summary.