Discussion on fixing a transaction mutation error related to incrementing a field that was not present in Sanity.io

25 replies
Last updated: Jun 4, 2020
I have a transaction mutation that was working up until a few days ago and not sure if something changed in the sanity client or if I should be building my chained transactions differently? but getting a
Sanity error: s: The mutation(s) failed: Cannot increment "content.main.tickets" because it is not present
AI Update

I understand the frustration - this error appearing suddenly on code that was working is definitely confusing. The "Cannot increment because it is not present" error occurs when you try to use inc() on a field that doesn't exist yet in the document.

Since you mentioned this was working until a few days ago, let me address both the technical issue and the "why did it break" question.

Why it might have stopped working:

If your code was genuinely working before, the most likely explanations are:

  • The documents themselves changed (perhaps new documents are being created without those fields initialized, or a migration removed them)
  • Your document creation/initialization logic changed
  • The specific documents you're now operating on have a different structure than before

The inc operation itself requires the field to exist - this behavior hasn't changed in the Sanity client.

The solution: Ensure the field exists before incrementing

According to the official Patches documentation, you need to use setIfMissing to create the field if it doesn't exist. The docs specifically show this pattern:

client
  .patch(documentId)
  .setIfMissing({ 'stats.visitorCount': 0 })
  .inc({ 'stats.visitorCount': 1 })
  .commit()

For your nested path content.main.tickets, you need to ensure the entire structure exists. The safest approach is to set defaults at each level:

client
  .patch(documentId)
  .setIfMissing({ content: {} })
  .setIfMissing({ 'content.main': {} })
  .setIfMissing({ 'content.main.tickets': 0 })
  .inc({ 'content.main.tickets': 1 })
  .commit()

This builds up the nested structure step by step, ensuring each parent exists before going deeper.

Alternative approach:

You can also try using nested object syntax for the initial structure:

client
  .patch(documentId)
  .setIfMissing({ content: { main: { tickets: 0 } } })
  .inc({ 'content.main.tickets': 1 })
  .commit()

Debugging next steps:

To figure out what changed:

  1. Query the document before the patch to see if content.main exists
  2. Review any recent schema changes or content migrations
  3. Check your document creation logic to ensure the nested structure is initialized
  4. Look for any recent updates to @sanity/client in your dependencies (though the API behavior itself hasn't changed)

The key takeaway: setIfMissing only works if parent objects already exist. For deeply nested paths, you need to ensure each level of the structure is present before you can increment values within it.

and on the object i am passing the patch i do have an empty field at content.main.tickets
does it need a default value if i am incrementing it?
seems i have to set before i inc,

    .patch(customer.id.toString(), patch => patch.setIfMissing(customerTickets))
    .patch(customer.id.toString(), patch => patch.inc(customerTickets))
It needs to be already set β€”
inc
will fail if the field doesn't exist or if it's not a number
Nothing has changed here (at least not deliberately!), did your code change?
Unrelated: Should you not be calling
setIfMissing
with
0
?
yes! sorry was just testing
i realize i must have added the inc after my initial tests
so did not notice the fail for new transaction
Is it still failing with
setIfMissing
+
inc
?
nope!
all good now
You can also multiple patch types in a single patch. Something like:
await client
  .patch(customer.id.toString())
  .setIfMissing({tickets: 0})
  .inc({tickets: customerTickets})
  .commit()
Not sure what you're using to build your patch β€” the above is with the official JS client.
Oh yeah, it has a "chained mutations" thing, I haven't used that.
Same principle, though!
await client
  .patch(customer.id.toString(), patch =>
    patch
      .setIfMissing({tickets: 0})
      .inc({tickets: customerTickets}))
  .commit()
yep using that pattern
got this right now working:

  return client
    .transaction()
    .createIfNotExists(customerData)
    .patch(customer.id.toString(), patch => patch.set(customerObject))
    .patch(customer.id.toString(), patch => 
      patch
      .setIfMissing({"content.main.totalSpent": 0})
      .inc(customerSpent)
      .setIfMissing({"content.main.tickets": 0})
      .inc(customerTickets))
    .commit()
i'm sure i can clean that up but it works
Yep. You can also move the
patch.set(customerObject)
into the second one. Doesn't matter (results in same internal operations), just looks cleaner.
ah true they were all chained before
cleaning it up
πŸ‘

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?