it looks like our problems are caused by editors publishing drafts after backend token has updated docs, and thus overwriting it

20 replies
Last updated: Jan 28, 2026
Hi, we have a problem with drafts. By using the history api trying to make sense of that happens, it looks like our problems are caused by editors publishing drafts after backend token has updated docs, and thus overwriting it. Here’s what I think is going on:1. An editor starts editing a document, could be by accident, and then forgets to publish it.
2. Our server updates the document one or more times
3. Some editor that comes across the doc, publishes the draft.
It looks like this process
causes us to loose all the patches from step 2. And the time from 1-3 may be a few seconds (not likely for anything to happen at step 2), or days (more likely that our server did some work at step 2).
• I could encourage all editors to discard all drafts that they come across, and don’t publish anything that they haven’t edited the last few seconds.
This should keep the changes from step 2, right?• If editors are encouraged to keep drafts open only for a short period of time before publishing, the probability of something else being written to the doc in the meantime is very low. However, there’s still a small chance that step 2 will happen even though an editor is working with the doc only for a second or two, before publishing.
So what’s how do we prevent editors from overwriting updates our backend did?
AI Update

You've correctly identified the classic race condition problem with drafts overwriting backend updates. The good news is Sanity provides a robust solution through optimistic concurrency control using the ifRevisionID parameter.

The Core Solution: Use ifRevisionID for Backend Mutations

When your backend updates documents, you should use the ifRevisionID parameter in your patch mutations. This prevents the exact scenario you're describing:

// In your backend code
await client
  .patch(documentId)
  .ifRevisionID(currentRevisionId) // Only apply if this revision matches
  .set({ yourField: newValue })
  .commit()

If the revision ID doesn't match (because a draft exists or someone else modified it), the mutation will fail with a 409 Conflict status, allowing you to:

  • Fetch the latest version
  • Decide whether to merge changes or retry
  • Avoid silently losing data

Why This Happens

The issue stems from Sanity's draft system:

  1. When editors start editing, a draft document (prefixed with drafts.) is created
  2. Your backend likely updates the published document (without the drafts. prefix)
  3. When editors publish, the draft replaces the published document entirely
  4. Any backend changes made between steps 1-3 are lost

Additional Strategies

For your backend:

  • Always include ifRevisionID in mutations to detect conflicts
  • Consider checking if a draft exists before updating: query for drafts.{documentId} and handle accordingly
  • You might update both the draft and published versions if a draft exists

For your editors (workflow improvements):

  • Use custom document actions to warn editors if _updatedAt on the published doc is newer than when the draft was created
  • Implement a "Discard outdated drafts" action that checks timestamps
  • Add visual indicators in Studio showing when published content is newer than the draft

Alternative architecture: If your backend updates are frequent and critical, consider whether editors should be modifying those fields at all. You might:

  • Make certain fields read-only in Studio
  • Use field-level permissions
  • Structure your schema so backend-managed data lives in separate documents

Example: Detecting Conflicts

// Backend mutation with conflict handling
try {
  const doc = await client.getDocument(docId)
  await client
    .patch(docId)
    .ifRevisionID(doc._rev)
    .set({ backendField: value })
    .commit()
} catch (error) {
  if (error.statusCode === 409) {
    // Conflict detected - draft exists or doc changed
    console.log('Conflict: document was modified')
    // Handle: retry, merge, or alert
  }
}

The ifRevisionID approach is exactly what the Transactions documentation recommends for preventing lost updates in read-write cycles. It's Sanity's built-in solution for optimistic locking and will catch both draft conflicts and concurrent modifications.

Show original thread
20 replies

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?