Budibase/budibase · error · HTTPError
Project revision does not match.
Error message
Project revision does not match.
What it means
This 409 HTTPError is thrown when the _rev supplied to remove(id, rev) does not match the project's current revision in the workspace DB. It is an optimistic-concurrency guard ensuring the caller saw the latest state of the document before deleting it.
Source
Thrown at packages/server/src/sdk/workspace/projects/crud.ts:253
}
} catch (err) {
if (!(err instanceof HTTPError)) {
await rollbackAssignments(rollbacks)
}
throw err
}
return rollbacks
}
export async function remove(id: string, rev: string) {
const db = context.getWorkspaceDB()
const project = await get(id)
if (!project) {
throw new HTTPError(`Project with id '${id}' not found.`, 404)
}
if (project._rev !== rev) {
throw new HTTPError("Project revision does not match.", 409)
}
const rollbacks = await clearAssignments(id)
try {
return await db.remove(id, rev)
} catch (err) {
await rollbackAssignments(rollbacks)
throw err
}
}
View on GitHub (pinned to a81a902e9a)
Solutions
- Re-fetch the project with get(id) and pass its current _rev to remove()
- On 409, reload the document and retry the delete once with the fresh rev
- Ensure you read the project through the same workspace context you delete in, so revs come from the same DB
- Serialize delete operations (e.g. disable the delete button after first click) to avoid concurrent deletes
Example fix
// before await projects.remove(id, staleRev) // after const project = await projects.get(id) await projects.remove(id, project._rev)
Defensive patterns
Strategy: retry
Validate before calling
const current = await projects.get(id) if (current._rev !== rev) rev = current._rev // refresh before deleting
Type guard
function hasRev(p: unknown): p is Project & { _rev: string }
return !!p && typeof (p as Project)._rev === "string" Try / catch
try {
await projects.remove(id, rev)
} catch (e) {
if (e?.status === 409) {
const fresh = await projects.get(id)
if (fresh) return projects.remove(id, fresh._rev)
}
throw e
} Prevention
- Always pass the _rev from the most recent read, never a cached value
- Serialize destructive operations to avoid concurrent deletes
- Disable the delete action after first click; on 409 reload and retry once
- Ensure reads and writes use the same workspace context
When it happens
Trigger: Calling remove(id, rev) with an old or fabricated rev: the project was updated after the caller fetched it, the caller passed a rev from a different document, or a second delete attempt reuses a rev already consumed by the first successful delete.
Common situations: Two admins deleting the same app concurrently; a stale client cached the project list before another user edited the app; retrying a delete after the first one partially succeeded; passing `undefined`/wrong property (e.g. the project object instead of its _rev).
Related errors
- Unable to bulk remove documents: ${res.error}
- Revision is required for updates
- Custom REST template cannot be deleted while it is in use
- A row action with the same name already exists.
- DB does not exist
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/9f45e757776578c5.
Report an issue: GitHub.