{"record":{"id":"865d8f6e4c6b7f4c","repo":"Budibase/budibase","slug":"cannot-set-role-of-account-holder","errorCode":null,"errorMessage":"Cannot set role of account holder","messagePattern":"Cannot set role of account holder","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/worker/src/api/controllers/global/users.ts","lineNumber":93,"sourceCode":"  }))\n}\n\nexport const save = async (ctx: UserCtx<UnsavedUser, SaveUserResponse>) => {\n  try {\n    const currentUserId = ctx.user?._id\n    const tenantId = context.getTenantId()\n    const requestUser: User = { ...ctx.request.body, tenantId }\n\n    // Do not allow the account holder role to be changed\n    if (\n      requestUser.admin?.global !== true ||\n      requestUser.builder?.global !== true\n    ) {\n      const accountMetadata = await users.getExistingAccounts([\n        requestUser.email,\n      ])\n      if (accountMetadata?.length > 0) {\n        throw Error(\"Cannot set role of account holder\")\n      }\n    }\n\n    const user = await userSdk.db.save(requestUser, { currentUserId })\n\n    ctx.body = {\n      _id: user._id!,\n      _rev: user._rev!,\n      email: user.email,\n    }\n  } catch (err: any) {\n    ctx.throw(err.status || 400, err?.message || err)\n  }\n}\n\nexport const changeTenantOwnerEmail = async (\n  ctx: Ctx<ChangeTenantOwnerEmailRequest, void>\n) => {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/users.ts#L75-L111","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: stripping owner roles\nawait saveUser({ email: ownerEmail, admin: { global: false }, builder: { global: false } })\n// after: keep owner flags intact\nawait saveUser({ email: ownerEmail, admin: { global: true }, builder: { global: true } })","handlingStrategy":"try-catch","validationCode":"// skip account holders before submitting role updates\nconst accountHolders = await getAccountHolderEmails()\nconst updatable = users.filter(u => !accountHolders.includes(u.email) ||\n  (u.admin?.global === true && u.builder?.global === true))","typeGuard":"const isAccountHolderDowngrade = (u: UnsavedUser, holders: string[]): boolean =>\n  holders.includes(u.email) &&\n  (u.admin?.global !== true || u.builder?.global !== true)","tryCatchPattern":"try {\n  await api.saveUser(payload)\n} catch (e) {\n  if (String(e?.message).includes(\"Cannot set role of account holder\")) {\n    notify(\"The account holder's roles cannot be reduced\")\n  }\n}","preventionTips":["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"],"tags":["authorization","roles","worker","account-holder"],"backgroundTag":"protected-account-role-change","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}