Core Concepts
Conflict resolution
What happens when an offline edit and a server side change land on the same record. This page covers the two pass handshake that detects it, the priority chain that decides what to do about it, and the one thing conflict resolution is never allowed to touch.
Prerequisites
- Familiarity with core_concepts/offline_queue — conflicts only ever apply to queued mutations
- For wiring a resolution panel to this mechanism, see offline/conflict_ui
1
Why a conflict is even possible
Exactly one shape of event produces a conflict: an offline edit and a server side change landing on the same record during the same disconnected window.
typescript
2
Two strategies, one fork in the road
Set once, per store, at registration. The cost of guessing wrong is asymmetric — worth defaulting to
handshake when unsure.typescript
3
The two pass mechanics
The entire handshake, end to end. The key property: this costs exactly 2 network requests regardless of queue size — 2 for one entry, 2 for a thousand.
typescript
4
The conflict priority chain
Every conflicted entry runs through this in order and stops at the first stage that handles it — worth knowing exactly where a given conflict will land before wiring anything up.
typescript
A frozen entry has no automatic timeout
Once an entry reaches the universal register (Stage 2), it stays frozen in IDB until resolveUnifiedConflict() is explicitly called — there is no fallback that fires on its own after some delay.
5
conflictStrategy — the automatic fallback
Only reached if neither of the first two stages is configured.
'merge' in particular is a deliberate dead end, not a shortcut — worth reading closely.typescript
6
Clock skew correction
Every timestamp in the manifest is corrected before it is sent — without this, a device with a drifting clock generates false conflicts out of nothing.
typescript
7
The atomic lock — why multi tab does not race
A subtlety that only shows up with more than one tab open. Without this, two tabs can both believe they are clear to write at the same time.
typescript
8
When the check itself fails
A failed revalidation and a missing one are treated very differently — worth knowing which failure mode you are looking at when debugging.
typescript
9
What this never covers
Worth stating plainly, since this mechanism can look like it solves a problem it deliberately does not.
typescript
For wiring a resolution UI to the universal register described in Step 4, see offline/conflict_ui. For the offline queue mechanics conflicts build on top of, see core_concepts/offline_queue. For the full API signature of resolveUnifiedConflict(), see api/resolve_unified_conflict.