Skip to content

Commit a3cacc9

Browse files
committed
fix(libsync): refresh lock state after 423 upload errors
Force remote rediscovery on both 412 and 423 upload errors so the lock state cleared from the journal is refreshed from the server, not only the bad etag on 412. Strengthen the 412 test to assert the parent folder etag is invalidated. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
1 parent 9f6b589 commit a3cacc9

2 files changed

Lines changed: 13 additions & 5 deletions

File tree

src/libsync/propagateupload.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -690,8 +690,9 @@ void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job)
690690
qCWarning(lcPropagateUpload) << replyContent; // display the XML error in the debug
691691

692692
if (_item->_httpErrorCode == 412 || _item->_httpErrorCode == 423) {
693-
// 412 or 423 can mean a stale lock token after the path moved under a renamed
694-
// parent. Clear it so the next sync does not retry with an invalid token.
693+
// 412 (precondition failed) and 423 (locked) can both follow a stale lock token
694+
// after the path moved under a renamed parent. Clear it so the next sync does not
695+
// retry with an invalid token.
695696
if (!_item->_lockToken.isEmpty()) {
696697
SyncJournalFileRecord record;
697698
if (propagator()->_journal->getFileRecord(_item->_file, &record) && record.isValid()) {
@@ -705,9 +706,9 @@ void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job)
705706
_item->_locked = SyncFileItem::LockStatus::UnlockedItem;
706707
}
707708

708-
if (_item->_httpErrorCode == 412) {
709-
propagator()->_journal->schedulePathForRemoteDiscovery(_item->_file);
710-
}
709+
// Force a fresh read of the remote state so a bad etag (412) is dropped and the
710+
// real lock state (423) is refreshed instead of the value cleared above.
711+
propagator()->_journal->schedulePathForRemoteDiscovery(_item->_file);
711712
propagator()->_anotherSyncNeeded = true;
712713
}
713714

test/testsyncmove.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,13 @@ private slots:
14911491
fakeFolder.serverErrorPaths().append("D/file.txt", 412);
14921492
QVERIFY(!fakeFolder.syncOnce()); // Upload fails with 412.
14931493

1494+
// The 412 handler must invalidate the parent folder etag so the next sync
1495+
// rediscovers it instead of trusting the DB.
1496+
SyncJournalFileRecord parent;
1497+
QVERIFY(fakeFolder.syncJournal().getFileRecord(QByteArray("D"), &parent));
1498+
QVERIFY(parent.isValid());
1499+
QCOMPARE(parent._etag, QByteArrayLiteral("_invalid_"));
1500+
14941501
// After clearing the error, the next sync must succeed.
14951502
fakeFolder.serverErrorPaths().clear();
14961503
[[maybe_unused]] const auto blacklistWiped = fakeFolder.syncJournal().wipeErrorBlacklist();

0 commit comments

Comments
 (0)