Skip to content

Comments

All: Fixes #13531: When creating a conflict, ensure the latest note contents are used to create the conflict#13552

Merged
laurent22 merged 6 commits intolaurent22:devfrom
mrjo118:reload-note-before-conflict
Nov 3, 2025
Merged

All: Fixes #13531: When creating a conflict, ensure the latest note contents are used to create the conflict#13552
laurent22 merged 6 commits intolaurent22:devfrom
mrjo118:reload-note-before-conflict

Conversation

@mrjo118
Copy link
Contributor

@mrjo118 mrjo118 commented Oct 27, 2025

The conflict resolution process handles conflicts for notes by creating a conflict using the local note contents and then replaces the local note with the remote note. Currently, the process will select the local note contents during the upload step of the Synchroniser code. This means that if there are many uploads queued, it is possible that if a conflict is created for a note which is currently being edited by a user, an outdated version of the note will be used to create the conflicting note, and some text which the user has just typed may be lost.

Reproduction steps (this is easier to reproduce on desktop, due to the 15 second delay for sync as you type):

  1. Setup 2 Joplin clients syncing with OneDrive, with the default Welcome notebook. Have them both open
  2. Create a new note called 'Test' with the body '123'
  3. Sync both clients
  4. On client A, add 'a' to the end of the note body for note 'Test', then sync the change
  5. On client B, do not click sync. Duplicate the 5 default notes in the Welcome notebook twice
  6. Then, quickly add 'b' to the end of the note body for note 'Test'
  7. Wait until the sync automatically triggers. As soon as it triggers, quickly type '12345678901234567890' at the end of the note body for note 'Test'
  8. Once the sync completes, a conflict is created for note 'Test', but the note body only contains '123b'. It should contain '123b12345678901234567890'

In order to remedy this, this PR re-selects the local note immediately before creating the note conflict, so that the latest contents will be used.

This fixes #13531

Limitations

Note that if the user is typing continuously and they type faster than the debounce interval for the note editor (100ms desktop and 500ms on mobile), some changes may still be lost because they are not currently saved when the note is reloaded. Also, if the user happens to be typing just at the point when the conflict is created, the replacement of the local note contents with the remote contents may not have time to re-render, and therefore they are lost to the local version, due to a local save overwriting the change made by the conflict resolution. I'm not sure how this can be prevented to be honest, but it should be a rarer occurrence than the bug which this PR does address.

Testing

See videos following the reproduction steps.

Before:

issue.1.mp4

After:

issue.1.fix.mp4

}
} else if (action === SyncAction.NoteConflict) {
// Reload the note, to ensure the latest version is used to create the conflict
local = await Note.load(local.id);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would be difficult to add a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test now added

@laurent22
Copy link
Owner

That's great, thanks for adding the test @mrjo118!

@laurent22 laurent22 merged commit 4a17da3 into laurent22:dev Nov 3, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notes are being deleted

2 participants