immich-app/immich · error · Error

User not found

Error message

User not found

What it means

Thrown by NotificationService.sendTestEmail when userRepository.get(id, { withDeleted: false }) returns null. It is a plain Error (not a NestJS HttpException), so Nest's default exception filter maps it to HTTP 500 unless wrapped. The guard intentionally excludes soft-deleted users so test emails are never sent to deactivated accounts.

Source

Thrown at server/src/services/notification.service.ts:248

      });
    }
  }

  @OnEvent({ name: 'AlbumInvite' })
  async onAlbumInvite({ id, userId, senderName }: ArgOf<'AlbumInvite'>) {
    await this.jobRepository.queue({ name: JobName.NotifyAlbumInvite, data: { id, recipientId: userId, senderName } });
  }

  @OnEvent({ name: 'SessionDelete' })
  onSessionDelete({ sessionId }: ArgOf<'SessionDelete'>) {
    // after the response is sent
    setTimeout(() => this.websocketRepository.clientSend('on_session_delete', sessionId, sessionId), 500);
  }

  async sendTestEmail(id: string, dto: SystemConfigSmtpDto, tempTemplate?: string) {
    const user = await this.userRepository.get(id, { withDeleted: false });
    if (!user) {
      throw new Error('User not found');
    }

    try {
      await this.emailRepository.verifySmtp(dto.transport);
    } catch (error) {
      throw new BadRequestException('Failed to verify SMTP configuration', { cause: error });
    }

    const { server } = await this.getConfig({ withCache: false });
    const { html, text } = await this.emailRepository.renderEmail({
      template: EmailTemplate.TEST_EMAIL,
      data: {
        baseUrl: getExternalDomain(server),
        displayName: user.name,
      },
      customTemplate: tempTemplate!,
    });
    const { messageId } = await this.emailRepository.sendEmail({

View on GitHub (pinned to 199723261c)

Solutions

  1. Re-authenticate as an active admin so the caller's id resolves to a non-deleted user.
  2. Confirm the id passed to sendTestEmail is the currently authenticated user's id and not a stale value.
  3. Restore or re-create the soft-deleted user if the account should still exist.
  4. If exposing this to a wrapper service, fetch the user first and return a 404 instead of letting a bare Error surface.

Example fix

// before
const user = await this.userRepository.get(id, { withDeleted: false });
if (!user) {
  throw new Error('User not found');
}

// after (use a Nest HttpException so the client sees 404, not 500)
const user = await this.userRepository.get(id, { withDeleted: false });
if (!user) {
  throw new NotFoundException('User not found');
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling sendTestEmail, confirm the user exists and is not deleted.
const user = await this.userRepository.get(id, { withDeleted: false });
if (!user) {
  // do not call sendTestEmail; return a controlled 404 instead
  throw new NotFoundException('User not found');
}
await this.notificationService.sendTestEmail(id, dto, tempTemplate);

Type guard

// Narrow an Optional<User> to User before relying on it.
const isUser = (u: User | null): u is User => !!u && typeof u.id === 'string';

if (!isUser(user)) { /* skip */ }

Try / catch

// Wrap the bare Error so NestJS returns a controlled status.
try {
  await notificationService.sendTestEmail(id, dto);
} catch (e) {
  if (e instanceof Error && e.message === 'User not found') {
    throw new NotFoundException(e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /system/preferences/notifications/test (admin test-email endpoint) when the authenticated admin's user record was deleted, when an invalid/stale user id was passed, or when the id belongs to a soft-deleted user.

Common situations: The admin's account was deactivated while their session was still valid; a client passed a different user's id; the database row was removed out-of-band; a test harness uses a random UUID.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/37b8a27e19b21a94. Report an issue: GitHub.