Budibase/budibase · error
Cannot set role of account holder
Error message
Cannot set role of account holder
What it means
The worker's save user endpoint protects the account holder: if the user being saved is the registered account owner (found via users.getExistingAccounts by email) and the request does not grant full global admin+builder roles, it throws 'Cannot set role of account holder'. This prevents downgrading or stripping the roles of the account that owns the tenant/subscription.
Source
Thrown at packages/worker/src/api/controllers/global/users.ts:93
}))
}
export const save = async (ctx: UserCtx<UnsavedUser, SaveUserResponse>) => {
try {
const currentUserId = ctx.user?._id
const tenantId = context.getTenantId()
const requestUser: User = { ...ctx.request.body, tenantId }
// Do not allow the account holder role to be changed
if (
requestUser.admin?.global !== true ||
requestUser.builder?.global !== true
) {
const accountMetadata = await users.getExistingAccounts([
requestUser.email,
])
if (accountMetadata?.length > 0) {
throw Error("Cannot set role of account holder")
}
}
const user = await userSdk.db.save(requestUser, { currentUserId })
ctx.body = {
_id: user._id!,
_rev: user._rev!,
email: user.email,
}
} catch (err: any) {
ctx.throw(err.status || 400, err?.message || err)
}
}
export const changeTenantOwnerEmail = async (
ctx: Ctx<ChangeTenantOwnerEmailRequest, void>
) => {View on GitHub (pinned to a81a902e9a)
Solutions
- Leave the account holder with both admin.global and builder.global set to true in the update payload.
- Create a separate admin user first and manage roles through that account instead of modifying the owner.
- If ownership must change, use the account/tenant ownership transfer flow (e.g. changeTenantOwnerEmail) rather than editing roles directly.
- Filter the account holder's email out of bulk role-sync operations before submitting.
Example fix
// before: stripping owner roles
await saveUser({ email: ownerEmail, admin: { global: false }, builder: { global: false } })
// after: keep owner flags intact
await saveUser({ email: ownerEmail, admin: { global: true }, builder: { global: true } }) Defensive patterns
Strategy: try-catch
Validate before calling
// skip account holders before submitting role updates const accountHolders = await getAccountHolderEmails() const updatable = users.filter(u => !accountHolders.includes(u.email) || (u.admin?.global === true && u.builder?.global === true))
Type guard
const isAccountHolderDowngrade = (u: UnsavedUser, holders: string[]): boolean => holders.includes(u.email) && (u.admin?.global !== true || u.builder?.global !== true)
Try / catch
try {
await api.saveUser(payload)
} catch (e) {
if (String(e?.message).includes("Cannot set role of account holder")) {
notify("The account holder's roles cannot be reduced")
}
} Prevention
- Never remove admin.global or builder.global from the account owner
- Exclude owner emails from bulk role-sync/SCIM updates
- Provision a separate admin user for day-to-day administration
- Use the ownership transfer flow if ownership must change
When it happens
Trigger: PUT/POST to the global user save endpoint where requestUser.email matches an existing account holder AND (requestUser.admin?.global !== true || requestUser.builder?.global !== true) — i.e. any attempt to remove admin or builder global flags from the account holder.
Common situations: An admin trying to demote the account owner to a normal user or developer; a bulk role-update script hitting the owner account; syncing users from SSO/SCIM where the owner's roles would be overwritten with reduced scopes.
Related errors
- Unable to assign roles - license required.
- Unable to un-assign roles - license required.
- Tool is not available in this security context
- Slack OAuth authorization failed
- Preview role not found
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/865d8f6e4c6b7f4c.
Report an issue: GitHub.