immich-app/immich · error · BadRequestException
Email is not available
Error message
Email is not available
What it means
Thrown by UserAdminService.update when the requested new email is already used by a different account. Uniqueness of emails is enforced per instance; the check excludes the target user itself (duplicate.id !== id). It is a 400 BadRequest for a uniqueness conflict.
Solutions
- Choose a different email or free up the address by updating/removing the conflicting account first
- Check availability with a user lookup by email before submitting the update
- If the duplicate account is stale, delete it, then retry the email change
Example fix
// before
await adminApi.updateUser(userId, { email: 'shared@corp.com' });
// after
const existing = await adminApi.getUserByEmail('shared@corp.com');
if (existing && existing.id !== userId) throw new Error('email taken');
await adminApi.updateUser(userId, { email: 'shared@corp.com' }); Defensive patterns
Strategy: validation
Validate before calling
const existing = await adminApi.getUserByEmail(dto.email);
if (existing && existing.id !== id) {
throw new Error(`email ${dto.email} is already in use`);
} Try / catch
try { await adminApi.updateUser(id, dto); } catch (e) {
if (e.response?.status === 400 && /Email is not available/.test(e.response?.data?.message ?? '')) {
// prompt for a different email or resolve the conflicting account
}
throw e;
} Prevention
- Check email availability before submitting updates
- Maintain unique-email enforcement at the input layer
- Resolve or remove conflicting accounts during migrations
When it happens
Trigger: PUT/PATCH /api/admin/users/:id with dto.email equal to the email of another existing user. Also triggered by case/format variants that resolve to an existing account, or re-inviting a user whose email was taken by a deleted-but-restored account.
Common situations: Merging accounts or changing a user's email to one already registered; renaming a user to a former employee's address that still exists; duplicate registrations before the update attempt.
Related errors
- A tag with that name already exists
- Email is not available
- Email is not available
- Storage label already in use by another account
- Cannot merge a person into themselves
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/a64d9f36ea2843c7.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/user-admin.service.ts:71
return mapUserAdmin(user);
}
async update(auth: AuthDto, id: string, dto: UserAdminUpdateDto): Promise<UserAdminResponseDto> {
const user = await this.findOrFail(id, {});
if (dto.isAdmin !== undefined && dto.isAdmin !== auth.user.isAdmin && auth.user.id === id) {
throw new BadRequestException('Admin status can only be changed by another admin');
}
if (dto.quotaSizeInBytes && user.quotaSizeInBytes !== dto.quotaSizeInBytes) {
await this.userRepository.syncUsage(id);
}
if (dto.email) {
const duplicate = await this.userRepository.getByEmail(dto.email);
if (duplicate && duplicate.id !== id) {
this.logger.debug('Email already in use by another account');
throw new BadRequestException('Email is not available');
}
}
if (dto.storageLabel) {
const duplicate = await this.userRepository.getByStorageLabel(dto.storageLabel);
if (duplicate && duplicate.id !== id) {
throw new BadRequestException('Storage label already in use by another account');
}
}
if (dto.password) {
dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
}
if (dto.pinCode) {
dto.pinCode = await this.cryptoRepository.hashBcrypt(dto.pinCode, SALT_ROUNDS);
}
View on GitHub (pinned to e55ac299a4)