{"record":{"id":"37b8a27e19b21a94","repo":"immich-app/immich","slug":"user-not-found-37b8a2","errorCode":null,"errorMessage":"User not found","messagePattern":"User not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/notification.service.ts","lineNumber":248,"sourceCode":"      });\n    }\n  }\n\n  @OnEvent({ name: 'AlbumInvite' })\n  async onAlbumInvite({ id, userId, senderName }: ArgOf<'AlbumInvite'>) {\n    await this.jobRepository.queue({ name: JobName.NotifyAlbumInvite, data: { id, recipientId: userId, senderName } });\n  }\n\n  @OnEvent({ name: 'SessionDelete' })\n  onSessionDelete({ sessionId }: ArgOf<'SessionDelete'>) {\n    // after the response is sent\n    setTimeout(() => this.websocketRepository.clientSend('on_session_delete', sessionId, sessionId), 500);\n  }\n\n  async sendTestEmail(id: string, dto: SystemConfigSmtpDto, tempTemplate?: string) {\n    const user = await this.userRepository.get(id, { withDeleted: false });\n    if (!user) {\n      throw new Error('User not found');\n    }\n\n    try {\n      await this.emailRepository.verifySmtp(dto.transport);\n    } catch (error) {\n      throw new BadRequestException('Failed to verify SMTP configuration', { cause: error });\n    }\n\n    const { server } = await this.getConfig({ withCache: false });\n    const { html, text } = await this.emailRepository.renderEmail({\n      template: EmailTemplate.TEST_EMAIL,\n      data: {\n        baseUrl: getExternalDomain(server),\n        displayName: user.name,\n      },\n      customTemplate: tempTemplate!,\n    });\n    const { messageId } = await this.emailRepository.sendEmail({","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/notification.service.ts#L230-L266","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-authenticate as an active admin so the caller's id resolves to a non-deleted user.","Confirm the id passed to sendTestEmail is the currently authenticated user's id and not a stale value.","Restore or re-create the soft-deleted user if the account should still exist.","If exposing this to a wrapper service, fetch the user first and return a 404 instead of letting a bare Error surface."],"exampleFix":"// before\nconst user = await this.userRepository.get(id, { withDeleted: false });\nif (!user) {\n  throw new Error('User not found');\n}\n\n// after (use a Nest HttpException so the client sees 404, not 500)\nconst user = await this.userRepository.get(id, { withDeleted: false });\nif (!user) {\n  throw new NotFoundException('User not found');\n}","handlingStrategy":"validation","validationCode":"// Before calling sendTestEmail, confirm the user exists and is not deleted.\nconst user = await this.userRepository.get(id, { withDeleted: false });\nif (!user) {\n  // do not call sendTestEmail; return a controlled 404 instead\n  throw new NotFoundException('User not found');\n}\nawait this.notificationService.sendTestEmail(id, dto, tempTemplate);","typeGuard":"// Narrow an Optional<User> to User before relying on it.\nconst isUser = (u: User | null): u is User => !!u && typeof u.id === 'string';\n\nif (!isUser(user)) { /* skip */ }","tryCatchPattern":"// Wrap the bare Error so NestJS returns a controlled status.\ntry {\n  await notificationService.sendTestEmail(id, dto);\n} catch (e) {\n  if (e instanceof Error && e.message === 'User not found') {\n    throw new NotFoundException(e.message);\n  }\n  throw e;\n}","preventionTips":["Pass the authenticated user's own id, never an arbitrary one.","Filter soft-deleted users from your UI's user picker.","Use a Nest HttpException (NotFoundException) instead of bare Error for HTTP-facing guards."],"tags":["notification","smtp","user","nestjs","authentication"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}