Local API: file replaced via POST /items/{key}/file is never uploaded by file sync
Zotero 10.0.6, macOS, personal library with Zotero Storage file sync and auto-sync on.
1. Create an imported_file attachment and upload file A through the local API (If-None-Match: *). After auto-sync, A is on zotero.org. This works.
2. Replace the file through the local API with If-Match: md5(A) (authorize, upload, register). The responses are 200/201/204, and B is in the storage folder.
3. Wait for auto-sync and click Sync. The web API still reports md5 A, and GET /items/{key}/file serves A. The desktop shows itemAttachments.syncState = 2 and storageHash = md5(B).
4. Replace the file once more over the web API (If-Match: md5(A), file C). After the next desktop sync, C overwrites B in the storage folder. B never existed anywhere but this one machine and is gone without a conflict prompt.
registerUpload() in server_localAPI.js sets attachmentSyncedHash / attachmentSyncedModificationTime to the new file's values but never touches attachmentSyncState, so _checkForUpdatedFile() sees the file as matching the synced hash. Even when the state is set to upload, ZFS would send If-Match: md5(B) and get a conflict. The initial upload works only via the 412-without-version retry. Reading _checkAttachmentForDownload(), the next remote change to the item would download A over B. Possible fix: compare If-Match against the current file's MD5 (as GET reports it), leave the synced fields alone, and mark the attachment to_upload.
1. Create an imported_file attachment and upload file A through the local API (If-None-Match: *). After auto-sync, A is on zotero.org. This works.
2. Replace the file through the local API with If-Match: md5(A) (authorize, upload, register). The responses are 200/201/204, and B is in the storage folder.
3. Wait for auto-sync and click Sync. The web API still reports md5 A, and GET /items/{key}/file serves A. The desktop shows itemAttachments.syncState = 2 and storageHash = md5(B).
4. Replace the file once more over the web API (If-Match: md5(A), file C). After the next desktop sync, C overwrites B in the storage folder. B never existed anywhere but this one machine and is gone without a conflict prompt.
registerUpload() in server_localAPI.js sets attachmentSyncedHash / attachmentSyncedModificationTime to the new file's values but never touches attachmentSyncState, so _checkForUpdatedFile() sees the file as matching the synced hash. Even when the state is set to upload, ZFS would send If-Match: md5(B) and get a conflict. The initial upload works only via the 412-without-version retry. Reading _checkAttachmentForDownload(), the next remote change to the item would download A over B. Possible fix: compare If-Match against the current file's MD5 (as GET reports it), leave the synced fields alone, and mark the attachment to_upload.
Upgrade Storage