# Resolve conflicts

> When a commit conflicts, what the markers and the structured error look like, how to resolve them, and why a lost race is not a conflict.

Canonical URL: https://www.slivingdoc.dev/docs/guides/conflicts/ · Version: 0.1 · Updated: 2026-09-21

Full site index: https://www.slivingdoc.dev/llms.txt

A conflict is the one case slivingdoc refuses to decide for you: your
changes and the accepted remote state changed the same lines of the same
file. The operation returns `CONTENT_CONFLICT`, rewrites the visible
directory with the merged result, and leaves conflict markers in the
affected files. Nothing is lost, and nothing is accepted.

![Two writers change the same lines; the commit returns conflict markers in the file for the caller to resolve](/diagrams/conflict.svg)

## What you get back

The affected files contain standard markers:

```text
<<<<<<< local
the caller's text
=======
the accepted remote text
>>>>>>> remote
```

A complete marker block is these exact lines, at column zero and in this
order. The scanner accepts LF and CRLF line endings, finds every
complete, non-nested block in a file, and counts rows one-based and
inclusive. Changing one character in any of the three signature lines
makes that block ordinary text again.

The CLI reports it like any other domain error:

```text
CONTENT_CONFLICT · MERGE_CONFLICT
Resolve the conflict blocks before notes_commit.
  notes/today.md  conflict  lines 12-18, 40-42
next: edit the files, then commit
retryable: false
```

An agent decodes the same facts as structured data:

```json
{
  "code": "CONTENT_CONFLICT",
  "reason": "MERGE_CONFLICT",
  "action": "EDIT_FILES",
  "files": [
    {
      "path": "notes/today.md",
      "reason": "TEXT_CONFLICT",
      "ranges": [{ "start": 12, "end": 18 }]
    }
  ]
}
```

The two reasons at the top level are `MERGE_CONFLICT`, for a conflict
found by the three-tree merge, and `UNRESOLVED_MARKERS`, for a complete
marker block that was already in the visible directory before any merge
ran. Both carry the action `EDIT_FILES`. Each file entry names its own
reason:

| File reason          | Means                                                                 |
| -------------------- | --------------------------------------------------------------------- |
| `TEXT_CONFLICT`      | A text conflict, with the marker ranges to resolve.                   |
| `PATH_CONFLICT`      | A file-versus-directory conflict. The `ranges` array is always empty. |
| `UNRESOLVED_MARKERS` | A marker block that was already in the directory when the commit ran. |

## Resolve it

Resolve the files with ordinary file tools. No Git command is involved,
and there is nothing to abort or continue.

1. Open each file the error named, at the line ranges it gave.
2. Keep the text you want and delete the three marker lines.
3. Commit again.

```text
export SLIVINGDOC_BUCKET=my-notes
# edit notes/today.md, remove the marker lines
slivingdoc commit notes -m "merge today's notes"
```

The resolved directory becomes your local intent. If the remote state
moved again while you were editing, the server merges your directory
against the new state, and you may be handed another conflict to resolve
— on the newer text.

> **Warning:** A complete marker block is never accepted into the
> notebook, even one you wrote by hand and meant to keep. A commit that
> still contains one is refused with the reason `UNRESOLVED_MARKERS`
> before anything is published. Documenting the marker syntax in a note
> therefore needs one character changed in a signature line.

## A lost race is not a conflict

![Two writers publish at once; the loser merges against the new head and retries, and only overlapping lines become a conflict](/diagrams/race.svg)

Two writers that publish at the same moment both observed the same
manifest, and the object store accepts only one replacement for it. The
loser gets a precondition failure. That is expected contention, not a
storage failure and not a conflict: the losing writer downloads what it
missed, merges against the new head, waits a bounded randomized backoff,
and retries by itself. You see nothing.

It only becomes a `CONTENT_CONFLICT` when the merge finds that both
writers changed the same lines. Retries have fixed, configurable bounds —
see `--commit-retries` in
[Configuration](/docs/reference/configuration/). Exhausting them returns
an error and keeps your files available for another attempt.

> **Note:** A file under a configured read-only path never carries a
> `TEXT_CONFLICT` or a `PATH_CONFLICT`: pull always takes the remote's
> side there, so the merge sees identical content and reports nothing. A
> marker block found there by a commit is still refused as
> `UNRESOLVED_MARKERS`.

## Next

- [Errors](/docs/reference/errors/) — every code, reason, and action.
- [Guarantees and limits](/docs/concepts/guarantees/) — what "never
  silently overwritten" is promised to mean.
- [Use the CLI without a host](/docs/guides/cli/) — reading the reports
  in full.
