Guides / Conflict rules
Conflict rules
What happens when the same row changed on both sides since the last sync, and how to tell Tidewater which side wins.
A conflict is a row that has a newer updated_at on both the source and the replica than the timestamp of the last successful sync. Tidewater never guesses: without a rule it stops, lists the rows and exits with code 4.
Rules apply per table. A table without a rule inherits the default rule, and if there is none, the sync stops on the first conflict.
#Declaring a rule
Rules live in tidewater.toml under the table they apply to. The rule names a winner and, optionally, columns that are merged instead of overwritten.
toml# tidewater.toml [tables.orders] conflict = "source" # source | replica | newest | manual merge = ["notes", "tags"] # these columns are concatenated, not chosen [tables.customers] conflict = "newest"
Winners
| Value | Type | Meaning |
|---|---|---|
| sourcedefault | string | The source row overwrites the replica. The replica's version is kept in the journal for 30 days. |
| replica | string | The replica row is pushed back to the source. Needs write access on the source. |
| newest | string | Whichever updated_at is later. Clocks must be within 5 s of each other or the sync refuses to run. |
| manual | string | Stop and write the conflicting pairs to .tidewater/conflicts/ for a person to resolve. |
#Merging columns
Some columns should not have a winner. Free-text notes, tag lists and counters are better combined. The merge array names them; the rest of the row still follows the winner.
Merging a numeric column adds the two deltas since the last sync, not the two values. A counter that went 10 → 12 on one side and 10 → 15 on the other ends at 17, not 27.
#Seeing what a rule would do
Before trusting a rule on a large table, run the sync with --dry-run. It prints every conflict and the row that would win, and changes nothing.
shell$ tidewater sync --table orders --dry-run 3 conflicts in orders #4471 source wins (replica changed 2 columns: status, notes → merged) #4480 source wins (replica changed 1 column: status) #4502 source wins (replica changed 4 columns) dry run — nothing written
The same output is written to the journal on a real run, so the answer to "why does this row look like that" is always one tidewater log #4471 away.
Last edited 19 September 2026 · Edit this page