{"record":{"id":"20e483033efcc16b","repo":"Budibase/budibase","slug":"email-already-in-use-email-20e483","errorCode":null,"errorMessage":"Email already in use: '${email}'","messagePattern":"Email already in use: '(.+?)'","errorType":"exception","errorClass":"EmailUnavailableError","httpStatus":null,"severity":"error","filePath":"packages/backend-core/src/users/utils.ts","lineNumber":71,"sourceCode":"  groups?: UserGroup[]\n) {\n  const userGroups = groups?.filter(\n    group => user.userGroups?.indexOf(group._id!) !== -1\n  )\n  if (userGroups && userGroups.length > 0) {\n    return userGroups.some(group =>\n      Object.values(group.roles || {}).includes(\"CREATOR\")\n    )\n  }\n  return false\n}\n\nexport async function validateUniqueUser(email: string, tenantId: string) {\n  // check budibase users in other tenants\n  if (env.MULTI_TENANCY) {\n    const tenantUser = await getFirstPlatformUser(email)\n    if (tenantUser != null && tenantUser.tenantId !== tenantId) {\n      throw new EmailUnavailableError(email)\n    }\n  }\n\n  // check root account users in account portal\n  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {\n    const account = await accountSdk.getAccount(email)\n    if (account && account.verified && account.tenantId !== tenantId) {\n      throw new EmailUnavailableError(email)\n    }\n  }\n}\n\n/**\n * For a list of users, return the account holder if there is an email match.\n */\nexport async function getAccountHolderFromUsers(\n  users: Array<UserIdentifier>\n): Promise<UserIdentifier | undefined> {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/users/utils.ts#L53-L89","documentation":"validateUniqueUser throws EmailUnavailableError when multi-tenancy is enabled and the requested email already belongs to a platform user in a DIFFERENT tenant. This prevents cross-tenant email collisions and user hijacking in shared-platform deployments.","triggerScenarios":"save() -> validateUniqueUser(email, tenantId) with env.MULTI_TENANCY=true and getFirstPlatformUser(email) returning a user whose tenantId differs from the caller's tenant - typically when creating a new user or changing a user's email.","commonSituations":"Inviting a user who signed up under another tenant in Budibase Cloud; moving users between tenants; self-hosted installations mistakenly run with MULTI_TENANCY=true; typos reusing a colleague's email already registered elsewhere.","solutions":["Use a different email address for the new tenant user","Delete/deactivate the user in the other tenant first (via that tenant's admin or the account portal)","Verify MULTI_TENANCY is intentionally true for your deployment; on single-tenant self-host, set it false","If the user legitimately migrated tenants, update their platform user's tenantId rather than re-inviting"],"exampleFix":"// before\nawait validateUniqueUser(\"shared@example.com\", \"tenantB\") // exists in tenantA\n// after\nconst existing = await getFirstPlatformUser(\"shared@example.com\")\nif (existing && existing.tenantId !== \"tenantB\") {\n  email = \"shared-tenantB@example.com\" // pick an unclaimed email\n}","handlingStrategy":"try-catch","validationCode":"// pre-check cross-tenant usage under multi-tenancy\nif (env.MULTI_TENANCY) {\n  const u = await getFirstPlatformUser(email)\n  if (u && u.tenantId !== currentTenantId) throw new Error(\"Email used in another tenant\")\n}","typeGuard":null,"tryCatchPattern":"import { EmailUnavailableError } from \"@budibase/backend-core\"\ntry {\n  await validateUniqueUser(email, tenantId)\n} catch (e) {\n  if (e instanceof EmailUnavailableError) {\n    // prompt for a different email or handle tenant migration\n  } else throw e\n}","preventionTips":["Under MULTI_TENANCY, expect globally unique emails and validate invites up front","Confirm MULTI_TENANCY matches your real deployment topology","For tenant migration, update the platform user's tenantId rather than re-inviting"],"tags":["multi-tenancy","email-conflict","uniqueness"],"backgroundTag":"email-already-in-use","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}