immich-app/immich · error · BadRequestException
Email is not available
Error message
Email is not available
What it means
A BadRequestException (HTTP 400) thrown by UserService.updateMe when the requested new email is already owned by a different user. The service looks up the email and, if a duplicate exists whose id differs from the acting user, rejects the change. This is a uniqueness guard for the self-service profile update path.
Source
Thrown at server/src/services/user.service.ts:61
async getMe(auth: AuthDto): Promise<UserAdminResponseDto> {
const user = await this.userRepository.get(auth.user.id, {});
if (!user) {
throw new BadRequestException('User not found');
}
return mapUserAdmin(user);
}
getCalendarHeatmap(auth: AuthDto, dto: CalendarHeatmapDto): Promise<CalendarHeatmapResponseDto> {
return getCalendarHeatmap(auth.user.id, dto, { asset: this.assetRepository });
}
async updateMe({ user }: AuthDto, dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
if (dto.email) {
const duplicate = await this.userRepository.getByEmail(dto.email);
if (duplicate && duplicate.id !== user.id) {
this.logger.warn('Email already in use by another account');
throw new BadRequestException('Email is not available');
}
}
const update: Updateable<UserTable> = {
email: dto.email,
name: dto.name,
avatarColor: dto.avatarColor,
};
if (dto.password) {
const hashedPassword = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
update.password = hashedPassword;
update.shouldChangePassword = false;
}
const updatedUser = await this.userRepository.update(user.id, update);
return mapUserAdmin(updatedUser);View on GitHub (pinned to 199723261c)
Solutions
- Prompt the user to pick a different email and re-submit.
- If the duplicate belongs to a soft-deleted account that should free the email, purge or hard-delete that account first.
- Verify case sensitivity expectations; trim and normalize the email client-side before sending.
- If the user believes the email is theirs, have an admin confirm ownership of the duplicate account before reassigning.
Example fix
// before
await api.updateMe({ email: 'taken@example.com' }); // 400
// after
const available = await api.checkEmailAvailable('taken@example.com');
if (!available) {
showEmailTakenError();
return;
}
await api.updateMe({ email: 'taken@example.com' }); Defensive patterns
Strategy: validation
Validate before calling
async function isEmailAvailable(email) {
// call any email-availability or user-search endpoint the API exposes
const taken = await api.searchUsers({ email });
return !taken.some((u) => u.email.toLowerCase() === email.trim().toLowerCase());
}
if (!(await isEmailAvailable(newEmail))) { showEmailTaken(); return; } Type guard
const isEmailNotAvailableError = (e: unknown): boolean => typeof e === 'object' && e !== null && (e as any).status === 400 && (e as any).message === 'Email is not available';
Try / catch
try {
await api.updateMe({ email: newEmail });
} catch (e) {
if (isEmailNotAvailableError(e)) {
setFieldError('email', 'This email is already in use');
return;
}
throw e;
} Prevention
- Normalize and trim the email client-side before submit.
- Check availability before letting the user submit the form when an endpoint exists.
- Show field-level errors for 400s so users can correct without a full retry.
When it happens
Trigger: PUT /users/me with an email that belongs to another account, including emails of soft-deleted users that still occupy the unique email column. Also triggered by case-variant collisions if the lookup is case-insensitive.
Common situations: User picks an email already registered; merging two accounts by renaming one to the other's email; an admin pre-reserved an email; a previously deleted user's email still in the table.
Related errors
- User not found
- Invalid SMTP configuration
- Invalid user
- Unsupported file type ${filename}
- May not request original file
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/4f63f5cfebf0f691.
Report an issue: GitHub.