immich-app/immich · error · Error

User not found

Error message

User not found

What it means

Thrown by NotificationService.sendTestEmail when the user repository returns no (non-deleted) user for the given id. The service raises a plain Error('User not found') before attempting SMTP verification, so no test email can be prepared for a nonexistent recipient.

Solutions

  1. Confirm the target user id exists and is not soft-deleted (list users via the admin API and use a current id)
  2. Refresh the admin UI user list to drop stale ids
  3. If the user was deleted, restore them or pick an active admin account for the test email
  4. Check you are calling the correct Immich instance/environment

Example fix

// before
await api.notificationApi.sendTestEmail(deletedUserId, smtpDto); // throws 'User not found'
// after
const users = await api.userApi.getAllUsers(false); // non-deleted users
const target = users.find(u => u.email === 'admin@example.com');
await api.notificationApi.sendTestEmail(target.id, smtpDto);
Defensive patterns

Strategy: validation

Validate before calling

const users = await api.userApi.getAllUsers(false); // exclude deleted
const target = users.find(u => u.id === userId);
if (!target) throw new Error(`User ${userId} not found or deleted; pick an active user`);

Type guard

function isActiveUser(user) {
  return !!user && typeof user.id === 'string' && user.id.length > 0 && user.deletedAt == null;
}

Try / catch

try {
  await api.notificationApi.sendTestEmail(userId, smtpDto);
} catch (e) {
  if (String(e.message).includes('User not found')) {
    console.warn(`User ${userId} missing/deleted; re-select recipient from user list`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the test-email endpoint with a user id that does not exist or belongs to a soft-deleted user (withDeleted: false lookup).

Common situations: Sending a test email right after a user account was deleted; stale id cached in the admin UI from before a user was removed; id copied from another Immich instance/environment; passing the wrong id type or truncated id.

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


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

Appendix: source

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

      level: NotificationLevel.Info,
      title: 'Cluster Group Request',
      description: `${senderName} asked you to join their cluster group`,
      data: JSON.stringify({ clusterGroupId }),
    });

    this.websocketRepository.clientSend('on_notification', userId, mapNotification(item));
  }

  @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 e55ac299a4)