Budibase/budibase · error · HTTPError

_id or _rev fields missing

Error message

_id or _rev fields missing

What it means

update() persists an edited automation by replacing the stored document, so both the CouchDB _id and _rev must be present; _rev is required for optimistic concurrency. It throws this 400 HTTPError when either is missing on the automation passed in.

Solutions

  1. Fetch the automation first and mutate the fetched object (which includes _id and _rev) before calling update().
  2. Ensure your HTTP client does not strip _id/_rev fields during serialization.
  3. Use the create endpoint for brand-new automations instead of update().

Example fix

// before
await api.post("/api/automations", { name: "my auto", ... }) // update call, no _id/_rev
// after
const existing = await api.get(`/api/automations/${id}`)
await api.put(`/api/automations`, { ...existing, ...changes }) // keeps _id and _rev
Defensive patterns

Strategy: validation

Validate before calling

function canUpdate(a: Automation): boolean {
  return typeof a._id === "string" && a._id.length > 0 && typeof a._rev === "string" && a._rev.length > 0
}
if (!canUpdate(automation)) throw new Error("fetch the automation before updating")
await sdk.automations.update(automation)

Type guard

const isPersisted = (a: Automation): a is Automation & { _id: string; _rev: string } =>
  typeof a._id === "string" && a._id.length > 0 && typeof a._rev === "string" && a._rev.length > 0

Try / catch

try {
  await sdk.automations.update(automation)
} catch (e) {
  if (e instanceof HTTPError && e.status === 400 && e.message.includes("_id or _rev")) {
    const fresh = await sdk.automations.get(automation._id!)
    return sdk.automations.update({ ...fresh, ...automation })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling the automation update endpoint / sdk.automations.update(automation) with an automation object lacking _id or _rev — e.g. a newly built object that was never fetched, or one stripped of metadata.

Common situations: Client constructed the automation from scratch instead of GET-then-PUT; serialization dropped underscore-prefixed fields; create/update endpoints confused (create doesn't need _rev, update does).

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/e86763730363b0cc. Report an issue: GitHub.

Appendix: source

Thrown at packages/server/src/sdk/workspace/automations/crud.ts:151

  automation = await checkForWebhooks({
    newAuto: automation,
  })
  const response = await db.put(automation)
  await events.automation.created(automation)
  for (let step of automation.definition.steps) {
    await events.automation.stepCreated(automation, step)
  }
  automation._rev = response.rev
  automation._id = response.id

  return maskAutomationSecrets(automation)
}

export async function update(automation: Automation) {
  automation = trimUnexpectedObjectFields(automation)
  validateStickyNoteLimit(automation)
  if (!automation._id || !automation._rev) {
    throw new HTTPError("_id or _rev fields missing", 400)
  }

  const db = getDb()

  const oldAutomation = await db.get<Automation>(automation._id)

  guardInvalidUpdatesAndThrow(automation, oldAutomation)

  automation = hydrateAutomationSecrets(automation, oldAutomation)
  automation = cleanAutomationInputs(automation)
  automation = await checkForWebhooks({
    oldAuto: oldAutomation,
    newAuto: automation,
  })
  const response = await db.put(automation)
  automation._rev = response.rev

  const oldAutoTrigger =

View on GitHub (pinned to a81a902e9a)