Budibase/budibase · error · HTTPError
Account holder cannot be deleted
Error message
Account holder cannot be deleted
What it means
destroy() throws HTTPError(400) 'Account holder cannot be deleted' when an admin (someone other than the account holder) attempts to delete the tenant's root account-holder user from within the product in a cloud deployment. The account holder record is owned by the account portal and cannot be removed in-app.
Source
Thrown at packages/backend-core/src/users/db.ts:577
})
return response
}
static async destroy(id: string) {
const db = getGlobalDB()
const dbUser = (await db.get(id)) as User
const userId = dbUser._id as string
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
// root account holder can't be deleted from inside budibase
const email = dbUser.email
const account = await accountSdk.getAccount(email)
if (account) {
if (dbUser.userId === getIdentity()!._id) {
throw new HTTPError('Please visit "Account" to delete this user', 400)
} else {
throw new HTTPError("Account holder cannot be deleted", 400)
}
}
}
await platform.users.removeUser(dbUser)
await db.remove(userId, dbUser._rev!)
const creatorsToDelete = (await isCreatorAsync(dbUser)) ? 1 : 0
await UserDB.quotas.removeUsers(1, creatorsToDelete)
await eventHelpers.handleDeleteEvents(dbUser)
await cache.user.invalidateUser(userId)
await sessions.invalidateSessions(userId, { reason: "deletion" })
}
static async createAdminUser(
email: string,
tenantId: string,View on GitHub (pinned to a81a902e9a)
Solutions
- Remove the account (and its holder) through the Account portal, which handles subscription/tenant teardown
- Keep the account-holder user and delete other users only
- In self-hosted setups this path is not reachable; ensure SELF_HOSTED/DISABLE_ACCOUNT_PORTAL env flags reflect the real deployment
Example fix
// before
await users.destroy(holderId) // holder, deleted by admin
// after
// skip account holders in bulk cleanup
if (!(await accountSdk.getAccount(user.email))) {
await users.destroy(user._id!)
} Defensive patterns
Strategy: try-catch
Validate before calling
// skip account holders in bulk deletion
const acct = await accountSdk.getAccount(user.email)
if (acct && user._id !== getIdentity()?._id) {
// this is the account holder being deleted by an admin - not allowed in-product
} Try / catch
try {
await users.destroy(userId)
} catch (e: any) {
if (e?.status === 400 && e?.message === "Account holder cannot be deleted") {
// remove via Account portal or skip this user in bulk jobs
} else throw e
} Prevention
- Filter out account holders (users whose email has an account) from bulk delete jobs
- Never attempt ownership transfer by delete-and-recreate of the holder
- In self-hosted deployments these errors should be unreachable - verify env flags
When it happens
Trigger: destroy() on a user whose email has an account in the account portal, where dbUser.userId differs from the current identity (i.e., an admin deleting the holder), with env.SELF_HOSTED=false and DISABLE_ACCOUNT_PORTAL=false.
Common situations: Admins offboarding the original signup user; bulk delete scripts that include the account holder; trying to transfer ownership by deleting and recreating the holder.
Related errors
- Please visit "Account" to delete this user
- Access denied to object store bucket.${err}
- Error activating license key: ${message}
- Forbidden
- Knowledge source downloads are disabled for this operation
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/70cbd2927da21f70.
Report an issue: GitHub.