Budibase/budibase · error · EmailUnavailableError

Email already in use: '${email}'

Error message

Email already in use: '${email}'

What it means

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.

Source

Thrown at packages/backend-core/src/users/utils.ts:71

  groups?: UserGroup[]
) {
  const userGroups = groups?.filter(
    group => user.userGroups?.indexOf(group._id!) !== -1
  )
  if (userGroups && userGroups.length > 0) {
    return userGroups.some(group =>
      Object.values(group.roles || {}).includes("CREATOR")
    )
  }
  return false
}

export async function validateUniqueUser(email: string, tenantId: string) {
  // check budibase users in other tenants
  if (env.MULTI_TENANCY) {
    const tenantUser = await getFirstPlatformUser(email)
    if (tenantUser != null && tenantUser.tenantId !== tenantId) {
      throw new EmailUnavailableError(email)
    }
  }

  // check root account users in account portal
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
    const account = await accountSdk.getAccount(email)
    if (account && account.verified && account.tenantId !== tenantId) {
      throw new EmailUnavailableError(email)
    }
  }
}

/**
 * For a list of users, return the account holder if there is an email match.
 */
export async function getAccountHolderFromUsers(
  users: Array<UserIdentifier>
): Promise<UserIdentifier | undefined> {

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Use a different email address for the new tenant user
  2. Delete/deactivate the user in the other tenant first (via that tenant's admin or the account portal)
  3. Verify MULTI_TENANCY is intentionally true for your deployment; on single-tenant self-host, set it false
  4. If the user legitimately migrated tenants, update their platform user's tenantId rather than re-inviting

Example fix

// before
await validateUniqueUser("shared@example.com", "tenantB") // exists in tenantA
// after
const existing = await getFirstPlatformUser("shared@example.com")
if (existing && existing.tenantId !== "tenantB") {
  email = "shared-tenantB@example.com" // pick an unclaimed email
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check cross-tenant usage under multi-tenancy
if (env.MULTI_TENANCY) {
  const u = await getFirstPlatformUser(email)
  if (u && u.tenantId !== currentTenantId) throw new Error("Email used in another tenant")
}

Try / catch

import { EmailUnavailableError } from "@budibase/backend-core"
try {
  await validateUniqueUser(email, tenantId)
} catch (e) {
  if (e instanceof EmailUnavailableError) {
    // prompt for a different email or handle tenant migration
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/20e483033efcc16b. Report an issue: GitHub.