{"record":{"id":"77a9a5601cb7c32b","repo":"Budibase/budibase","slug":"please-visit-account-to-delete-this-user","errorCode":null,"errorMessage":"Please visit \"Account\" to delete this user","messagePattern":"Please visit \"Account\" to delete this user","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/backend-core/src/users/db.ts","lineNumber":575,"sourceCode":"        })\n      }\n    })\n\n    return response\n  }\n\n  static async destroy(id: string) {\n    const db = getGlobalDB()\n    const dbUser = (await db.get(id)) as User\n    const userId = dbUser._id as string\n\n    if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {\n      // root account holder can't be deleted from inside budibase\n      const email = dbUser.email\n      const account = await accountSdk.getAccount(email)\n      if (account) {\n        if (dbUser.userId === getIdentity()!._id) {\n          throw new HTTPError('Please visit \"Account\" to delete this user', 400)\n        } else {\n          throw new HTTPError(\"Account holder cannot be deleted\", 400)\n        }\n      }\n    }\n\n    await platform.users.removeUser(dbUser)\n\n    await db.remove(userId, dbUser._rev!)\n\n    const creatorsToDelete = (await isCreatorAsync(dbUser)) ? 1 : 0\n    await UserDB.quotas.removeUsers(1, creatorsToDelete)\n    await eventHelpers.handleDeleteEvents(dbUser)\n    await cache.user.invalidateUser(userId)\n    await sessions.invalidateSessions(userId, { reason: \"deletion\" })\n  }\n\n  static async createAdminUser(","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/users/db.ts#L557-L593","documentation":"destroy() throws HTTPError(400) 'Please visit \"Account\" to delete this user' when the caller tries to delete their OWN user account while it is the verified root account holder in Budibase Cloud (not self-hosted, account portal enabled). Account holders must delete via the external Account portal so billing/tenancy is handled correctly.","triggerScenarios":"destroy() called on a user whose email matches an account in the account portal, where dbUser.userId equals the current identity's _id (self-deletion attempt), in a cloud deployment (env.SELF_HOSTED false and DISABLE_ACCOUNT_PORTAL false).","commonSituations":"Admin API DELETE /users/self in a cloud tenant; UI actions exposing self-delete; scripts cleaning up users that accidentally include the account-holder email.","solutions":["Delete the account via the Budibase Account portal (account.budibase.app) instead of the in-product API","Delete a different (non-account-holder) user, or have an admin remove the user while logged in as someone else","In self-hosted environments set SELF_HOSTED=true / DISABLE_ACCOUNT_PORTAL=true so this guard is bypassed legitimately"],"exampleFix":"// before\nawait users.destroy(accountHolderUserId) // called as the account holder\n// after\n// direct the user to the account portal; only destroy non-holder users in-product\nif (user.email !== accountHolderEmail) {\n  await users.destroy(user._id!)\n}","handlingStrategy":"try-catch","validationCode":"// check before attempting self-deletion\nconst isAccountHolder = user.email === (await accountSdk.getAccount(user.email))?.email\nif (isAccountHolder && !env.SELF_HOSTED) {\n  // direct the user to the Account portal instead of calling destroy\n}","typeGuard":null,"tryCatchPattern":"try {\n  await users.destroy(userId)\n} catch (e: any) {\n  if (e?.status === 400 && e?.message.includes(\"Account\\\" to delete\")) {\n    redirectToDeleteViaAccountPortal()\n  } else throw e\n}","preventionTips":["Hide/disable self-delete UI for account holders in cloud deployments","Check env.SELF_HOSTED/DISABLE_ACCOUNT_PORTAL to know which guard applies","Point users at account.budibase.app for account-level deletion"],"tags":["permissions","account-portal","deletion"],"backgroundTag":"account-holder-deletion-blocked","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}