{"record":{"id":"e86763730363b0cc","repo":"Budibase/budibase","slug":"id-or-rev-fields-missing","errorCode":null,"errorMessage":"_id or _rev fields missing","messagePattern":"_id or _rev fields missing","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/automations/crud.ts","lineNumber":151,"sourceCode":"  automation = await checkForWebhooks({\n    newAuto: automation,\n  })\n  const response = await db.put(automation)\n  await events.automation.created(automation)\n  for (let step of automation.definition.steps) {\n    await events.automation.stepCreated(automation, step)\n  }\n  automation._rev = response.rev\n  automation._id = response.id\n\n  return maskAutomationSecrets(automation)\n}\n\nexport async function update(automation: Automation) {\n  automation = trimUnexpectedObjectFields(automation)\n  validateStickyNoteLimit(automation)\n  if (!automation._id || !automation._rev) {\n    throw new HTTPError(\"_id or _rev fields missing\", 400)\n  }\n\n  const db = getDb()\n\n  const oldAutomation = await db.get<Automation>(automation._id)\n\n  guardInvalidUpdatesAndThrow(automation, oldAutomation)\n\n  automation = hydrateAutomationSecrets(automation, oldAutomation)\n  automation = cleanAutomationInputs(automation)\n  automation = await checkForWebhooks({\n    oldAuto: oldAutomation,\n    newAuto: automation,\n  })\n  const response = await db.put(automation)\n  automation._rev = response.rev\n\n  const oldAutoTrigger =","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/automations/crud.ts#L133-L169","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Fetch the automation first and mutate the fetched object (which includes _id and _rev) before calling update().","Ensure your HTTP client does not strip _id/_rev fields during serialization.","Use the create endpoint for brand-new automations instead of update()."],"exampleFix":"// before\nawait api.post(\"/api/automations\", { name: \"my auto\", ... }) // update call, no _id/_rev\n// after\nconst existing = await api.get(`/api/automations/${id}`)\nawait api.put(`/api/automations`, { ...existing, ...changes }) // keeps _id and _rev","handlingStrategy":"validation","validationCode":"function canUpdate(a: Automation): boolean {\n  return typeof a._id === \"string\" && a._id.length > 0 && typeof a._rev === \"string\" && a._rev.length > 0\n}\nif (!canUpdate(automation)) throw new Error(\"fetch the automation before updating\")\nawait sdk.automations.update(automation)","typeGuard":"const isPersisted = (a: Automation): a is Automation & { _id: string; _rev: string } =>\n  typeof a._id === \"string\" && a._id.length > 0 && typeof a._rev === \"string\" && a._rev.length > 0","tryCatchPattern":"try {\n  await sdk.automations.update(automation)\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 400 && e.message.includes(\"_id or _rev\")) {\n    const fresh = await sdk.automations.get(automation._id!)\n    return sdk.automations.update({ ...fresh, ...automation })\n  }\n  throw e\n}","preventionTips":["Always GET the automation before PUT","Don't strip underscore-prefixed fields in serializers","Use create for new automations, update only for fetched ones"],"tags":["validation","couchdb","automations","bad-request"],"backgroundTag":"missing-document-rev","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}