immich-app/immich · error · BadRequestException

Storage label already in use by another account

Error message

Storage label already in use by another account

What it means

Thrown by UserAdminService.update when the requested storageLabel is already assigned to a different account. Storage labels map users to unique storage paths, so duplicates would collide on disk layout. It is a 400 BadRequest for a uniqueness conflict.

Solutions

  1. Pick a unique storage label for the user
  2. Clear the label from the conflicting account first, then assign it
  3. Query users by storageLabel before assignment to verify availability

Example fix

// before
await adminApi.updateUser(newUser.id, { storageLabel: oldEmployeeLabel });
// after
const taken = users.find(u => u.storageLabel === oldEmployeeLabel && u.id !== newUser.id);
const label = taken ? `${oldEmployeeLabel}-2` : oldEmployeeLabel;
await adminApi.updateUser(newUser.id, { storageLabel: label });
Defensive patterns

Strategy: validation

Validate before calling

const taken = users.find(u => u.storageLabel === dto.storageLabel && u.id !== id);
if (taken) {
  throw new Error(`storage label ${dto.storageLabel} already in use`);
}

Try / catch

try { await adminApi.updateUser(id, dto); } catch (e) {
  if (e.response?.status === 400 && /Storage label already/.test(e.response?.data?.message ?? '')) {
    // pick a unique label or clear it on the conflicting account
  }
  throw e;
}

Prevention

When it happens

Trigger: PUT/PATCH /api/admin/users/:id with dto.storageLabel matching another user's storage label (duplicate.id !== id). Common when copying storage labels between users or reusing a label from a deleted account that still holds it.

Common situations: Migration scripts assigning external-library storage paths; an admin reusing an old employee's storage label for a new hire while the old account remains; typos where two users get the same label.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/cfeb8375601cf6c2. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/user-admin.service.ts:78

      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);
    }

    if (dto.storageLabel === '') {
      dto.storageLabel = null;
    }

    const updatedUser = await this.userRepository.update(id, { ...dto, updatedAt: new Date() });

    return mapUserAdmin(updatedUser);

View on GitHub (pinned to e55ac299a4)