immich-app/immich · error · BadRequestException
Email is not available
Error message
Email is not available
What it means
updateMe rejects an email change when the requested email is already owned by a different user account. The service looks up the email in the user repository and compares the found duplicate's id with the authenticated user's id. If a different account holds the email, the change is refused with a 400 Bad Request.
Solutions
- Pick a different email address that is not registered on this Immich instance.
- Check the existing account that owns the email (admin Users page) and free it up by changing that account's email first.
- If you are re-submitting your own unchanged email, omit the email field from the request body.
- In client code, handle the 400 with message 'Email is not available' and prompt the user to choose another address.
Example fix
// before
await api.updateMyUser({ email: 'shared@example.com' });
// after
const taken = await isEmailTaken('shared@example.com');
if (taken) throw new Error('choose a different email');
await api.updateMyUser({ email: 'unique@example.com' }); Defensive patterns
Strategy: validation
Validate before calling
const isEmailTaken = async (email: string) => {
const existing = await userRepo.getByEmail(email);
return !!existing;
};
if (dto.email && await isEmailTaken(dto.email)) {
throw new BadRequestException('Email is not available');
} Try / catch
try {
await api.updateMyUser({ email });
} catch (e) {
if (e instanceof BadRequestException && e.message === 'Email is not available') {
// prompt user to pick another email
}
} Prevention
- Always check email availability before submitting an update.
- Omit the email field when it hasn't changed.
- Normalize/trim emails client-side to avoid false collisions.
- In signup forms, enforce uniqueness at input time with a live availability check.
When it happens
Trigger: PUT /users/me with an email in the body whose value matches another existing user's email (duplicate.id !== user.id). Note that resubmitting your own current email is allowed since duplicate.id === user.id.
Common situations: Shared/family servers where two accounts try to use the same address; a user changing email to one that was later re-registered by an admin; seeding scripts importing users with colliding emails; stale client forms that pre-filled an old email now owned by someone else.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- Email is not available
- Email is not available
- Failed to verify SMTP configuration
- Failed to verify SMTP configuration
- Partner already exists
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/4f63f5cfebf0f691.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)