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
- Look up the existing user with getGlobalUserByEmail and operate on that _id instead
- Choose a different email if this is genuinely a new user
- If the email should be reassigned, delete/update the conflicting user first
- 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
- Fetch the user by email first when the _id may be stale
- Never echo a full user object back into save; send only changed fields
- Handle EmailUnavailableError distinctly in API clients with a 400-friendly message
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
- Email already in use: '${email}'
- Group name "${name}" is unavailable
- Error getting status
- Unable to remove doc without a valid _id and _rev.
- Cannot store document without _id field.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/ec3d4e554a066e00.
Report an issue: GitHub.