---
audience: developer
summary: A note shows different content in the editor vs. the rendered view, or stale
  content on reload.
title: Note content out of sync
path: troubleshooting/yjs-note-divergence
status: published
---

# Note content out of sync

The notes editor uses a Yjs CRDT for live multi-cursor collaboration.
The CRDT state is cached in Redis as a snapshot; the canonical body
is `Note.body` in MariaDB (written by REST autosave). Divergence
between the two is the cause of most "out of sync" symptoms.

## Symptom: Reload shows old content

You typed, you saw your text appear, you reload the page, and the
note rolls back to an older state.

Cause: REST autosave didn't fire (or failed silently); the CRDT
state had your edits but `Note.body` didn't. On reload, the editor
reconciles to `Note.body` and your edits are lost.

Mitigation: the editor watches for unsaved changes and warns on
navigation. If you ignored the warning, the unsaved text is gone.

## Symptom: Two devices show different content

Multiple devices editing the same note at once should sync via
Yjs. If they don't:

- **Yjs WebSocket disconnected** on one side. The editor shows a
  disconnected indicator; reconnect by reloading.
- **CRDT state diverged** because one side was offline for a long
  time. The reconciliation favours the latest server-side
  `Note.body` on first sync; offline edits in the offline client
  may be lost.

## Symptom: Editor shows different content than preview

Edit and Preview both render the same source; they should never
disagree. If they do:

- **Stale render cache** in the preview. Switch to Edit and back.
- **Markdown bug** — preview renderer mishandled something the
  CodeMirror editor renders fine. File a bug.

## Symptom: Note suddenly has someone else's edits I didn't see

That's how CRDT collaboration works — when two people edit a note
at the same time, both their edits merge. You may see a teammate's
text appear without a notification (notes don't notify on each
edit; only when comments are added or todos change).

## Symptom: `SW_NOTE_CONFLICT` on REST PUT

A REST PUT to update a note conflicted with concurrent edits via
the CRDT. The CRDT path is preferred — use the editor, or read the
current state via GET, merge your changes, and PUT again.

## Server-side: Yjs snapshot wipe

If the Redis-cached Yjs snapshots are stale (e.g. after a schema
migration), restart the API; on first boot a one-shot wipe runs
that clears them and reconciles from `Note.body`. The wipe runs
once per migration version (gated by a Redis flag); subsequent
restarts don't.

If you suspect Yjs state is corrupt for a single note:

```bash
.venv/bin/python -c "
import asyncio, redis.asyncio as aioredis
from app.config import Settings
async def main():
    s = Settings()
    r = aioredis.from_url(s.redis_url, decode_responses=False)
    await r.delete(b'sw:<tenant>:note_collab_snapshot:<note-id>')
    await r.aclose()
asyncio.run(main())
"
```

Re-open the note; it'll seed a fresh snapshot from `Note.body`.

## What's logged

- `notes.yjs_snapshot_wipe` — first-boot wipe on migration.
- `notes.crdt_reconcile_to_server` — editor seeded from `Note.body`.
- `notes.autosave_failed` — REST autosave PUT errored.

Greppable in your log aggregator.
