How do I resolve a 409 concurrency conflict?

Handle optimistic-concurrency 409 conflicts when writing to the Unison brain: re-read the current version, merge, and retry.

I got a 409 conflict error when writing a page. What do I do?

Unison uses optimistic concurrency control for page writes. If two writers try to update the same page simultaneously, one will win and the other will receive a 409. The response body will have "code": "conflict".

This is intentional — it prevents silent data loss from last-write-wins semantics.

The fix: read, merge, retry

  1. Re-read the current version of the page to get the latest content and version identifier.
  2. Merge your intended changes with the current version.
  3. Retry the write with the updated content.
async function writeWithRetry(path: string, newContent: string, token: string) {
  for (let attempt = 0; attempt < 3; attempt++) {
    // Read current version — capture contentHash for optimistic concurrency
    const current = await fetch(
      `https://brain.unisonlabs.ai/v1/brain/doc?path=${encodeURIComponent(path)}`,
      { headers: { Authorization: `Bearer ${token}` } },
    ).then(r => r.json());

    // Merge (application-specific logic)
    const merged = mergeContent(current.bodyMd, newContent);

    // Attempt in-place edit with expectedContentHash — 409 means someone else wrote first
    const res = await fetch(`https://brain.unisonlabs.ai/v1/brain/doc`, {
      method: 'PATCH',
      headers: { Authorization: `Bearer ${token}`, 'content-type': 'application/json' },
      body: JSON.stringify({
        path,
        oldStr: current.bodyMd,
        newStr: merged,
        expectedContentHash: current.contentHash,
      }),
    });

    if (res.ok) return res;
    if (res.status !== 409) throw new Error(`Unexpected status ${res.status}`);
    // 409 — re-read and retry
  }
  throw new Error('Concurrency conflict retries exhausted');
}

Avoiding conflicts in the first place

  • Use distinct paths. If different agents write different facts, give each its own page path rather than all writing to a single shared file.
  • Write to /private/ per actor. Each actor's private namespace is isolated, so per-user writes never conflict with each other.
  • Use append-style facts. For logs or timelines, append new entries at a unique timestamped subpath rather than rewriting the same file.

On this page