Budibase/budibase · error · EmailUnavailableError

Email already in use: '${email}'

Error message

Email already in use: '${email}'

What it means

save() throws EmailUnavailableError ("Email already in use: '<email>'") when no user document was found for the given _id but a user with the same email already exists in the global DB with a different _id. It guards the uniqueness of emails per tenant when saving users by id rather than email.

Source

Thrown at packages/backend-core/src/users/db.ts:273

      try {
        dbUser = await usersCore.getById(_id)
        if (email && dbUser.email !== email && !opts.allowChangingEmail) {
          throw new Error("Email address cannot be changed")
        }
      } catch (e: any) {
        if (e.status === 404) {
          // do nothing, save this new user with the id specified - required for SSO auth
        } else {
          throw e
        }
      }
    }

    if (!dbUser && email) {
      // no id was specified - load from email instead
      dbUser = await usersCore.getGlobalUserByEmail(email)
      if (dbUser && dbUser._id !== _id) {
        throw new EmailUnavailableError(email)
      }
    }

    const isNewUser = !dbUser
    const isEmailChanging = !!dbUser && !!email && dbUser.email !== email
    const shouldValidateUniqueUser =
      !opts.isAccountHolder && !!email && (isNewUser || isEmailChanging)

    // For new users, resolve effective group assignment before creator quota
    // calculation so creator-by-group users are counted correctly.
    let groupIdsToAssign = [...userGroups]
    if (isNewUser && groupIdsToAssign.length === 0) {
      const defaultGroup = await UserDB.groups.getDefaultGroup?.()
      if (defaultGroup?._id) {
        groupIdsToAssign = [defaultGroup._id]
      }
    }

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Look up the existing user with getGlobalUserByEmail and operate on that _id instead
  2. Choose a different email if this is genuinely a new user
  3. If the email should be reassigned, delete/update the conflicting user first
  4. Check whether the client cached an outdated _id and refresh the user record

Example fix

// before
await users.save({ _id: wrongId, email: "a@example.com" })
// after
const existing = await usersCore.getGlobalUserByEmail("a@example.com")
await users.save({ _id: existing!._id, email: "a@example.com" })
Defensive patterns

Strategy: try-catch

Validate before calling

// check availability before saving
const existing = await usersCore.getGlobalUserByEmail(email)
if (existing && existing._id !== targetId) {
  throw new Error(`Email ${email} is already used by user ${existing._id}`)
}

Try / catch

import { EmailUnavailableError } from "@budibase/backend-core"
try {
  await users.save({ _id, email })
} catch (e) {
  if (e instanceof EmailUnavailableError) {
    // load the conflicting user or prompt for a different email
  } else throw e
}

Prevention

When it happens

Trigger: Calling save({ _id }) with an email that belongs to a different existing user; updating a user's email to one already taken by another user (isEmailChanging path leads into validateUniqueUser / this check).

Common situations: Client sends a stale or wrong _id with a known email; inviting users whose email already exists; merging accounts; concurrent signups racing to claim the same email.

Related errors


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