{"record":{"id":"7b76704e56b0a09e","repo":"Budibase/budibase","slug":"project-revision-is-required","errorCode":null,"errorMessage":"Project revision is required.","messagePattern":"Project revision is required\\.","errorType":"validation","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/projects/crud.ts","lineNumber":98,"sourceCode":"    {\n      ...project,\n      color: normaliseProjectColor(project.color),\n      _id: docIds.generateProjectID(),\n      createdAt: now,\n      updatedAt: now,\n    },\n    { returnDoc: true }\n  )\n\n  return response.doc\n}\n\nexport async function update(project: UpdateProjectInput): Promise<Project> {\n  if (!project._id) {\n    throw new HTTPError(\"Project id is required.\", 400)\n  }\n  if (!project._rev) {\n    throw new HTTPError(\"Project revision is required.\", 400)\n  }\n  if (Object.hasOwn(project, \"name\")) {\n    validateProjectName(project.name)\n  }\n\n  const db = context.getWorkspaceDB()\n  const persisted = await get(project._id)\n  if (!persisted) {\n    throw new HTTPError(`Project with id '${project._id}' not found.`, 404)\n  }\n\n  const response = await db.put(\n    {\n      ...persisted,\n      ...project,\n      ...(Object.hasOwn(project, \"color\")\n        ? { color: normaliseProjectColor(project.color) }\n        : {}),","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/projects/crud.ts#L80-L116","documentation":"update() requires the current CouchDB _rev for optimistic concurrency; a missing _rev throws this 400 HTTPError. Without a revision the DB put could silently overwrite concurrent changes, so it is mandatory.","triggerScenarios":"Calling update with _id but no _rev — e.g. submitting a form payload that stripped underscore-prefixed fields, or constructing a fresh object with only the changed fields.","commonSituations":"Serializers/frameworks dropping _rev because of the leading underscore; clients caching a project without its revision; hand-built payloads from JSON that omitted _rev.","solutions":["Pass the _rev from the last fetched version of the project","Re-fetch the project to get a fresh _rev before updating","Fix payload serialisation so underscore-prefixed fields survive (whitelist rather than blacklist)","After a 409/conflict, re-fetch and retry with the new _rev"],"exampleFix":"// before\nawait sdk.projects.update({ _id: id, name: \"New\" })\n// after\nconst project = await sdk.projects.get(id)\nawait sdk.projects.update({ _id: id, _rev: project!._rev, name: \"New\" })","handlingStrategy":"validation","validationCode":"const hasRev = (p: { _rev?: string }): p is { _rev: string } =>\n  typeof p._rev === \"string\" && p._rev.length > 0\nif (!hasRev(payload)) throw new Error(\"Missing project _rev\")\nawait sdk.projects.update(payload)","typeGuard":"const hasRev = (p: { _rev?: string }): p is { _rev: string } =>\n  typeof p._rev === \"string\" && p._rev.length > 0","tryCatchPattern":"try {\n  await sdk.projects.update(payload as { _id: string; _rev: string })\n} catch (e) {\n  if (String(e.message).includes(\"revision is required\")) {\n    const fresh = await sdk.projects.get(payload._id)\n    if (fresh) await sdk.projects.update({ ...payload, _rev: fresh._rev })\n  }\n}","preventionTips":["Preserve underscore-prefixed fields through your serializers","Always spread a freshly fetched project into update payloads","After conflicts, re-fetch to obtain a new _rev rather than reusing old ones"],"tags":["validation","http-400","optimistic-concurrency","update"],"backgroundTag":"missing-required-field","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}