{"record":{"id":"5d6cb3ddbc75574c","repo":"immich-app/immich","slug":"failed-to-verify-smtp-configuration","errorCode":null,"errorMessage":"Failed to verify SMTP configuration","messagePattern":"Failed to verify SMTP configuration","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/notification-admin.service.ts","lineNumber":34,"sourceCode":"      level: dto.level ?? NotificationLevel.Info,\n      title: dto.title,\n      description: dto.description,\n      data: dto.data,\n    });\n\n    return mapNotification(item);\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({\n      to: user.email,\n      subject: 'Test email from Immich',\n      html,\n      text,\n      from: dto.from,\n      replyTo: dto.replyTo || dto.from,","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/notification-admin.service.ts#L16-L52","documentation":"Before sending a test email, Immich calls emailRepository.verifySmtp(dto.transport) to confirm the SMTP server is reachable and credentials work. If that throws (connection refused, auth failure, TLS handshake error, timeout), the service wraps it into 400 BadRequestException 'Failed to verify SMTP configuration' with the original error as cause, so the admin gets actionable feedback without revealing internal details.","triggerScenarios":"POST admin test-email (or saving SMTP settings) where the configured SMTP transport cannot be reached or rejects the connection: wrong host/port, bad credentials, STARTTLS mismatch, firewall blocking outbound SMTP.","commonSituations":"Typo in SMTP host; port 465/587 blocked by provider/firewall; username or app-password wrong; SMTP server requires TLS but config has plain; reverse DNS / SPF issues causing rejection.","solutions":["Read error.cause for the SMTP-level message (auth error, ECONNREFUSED, etc.).","Verify host, port, and security mode (SSL/465 vs STARTTLS/587) against your provider's docs.","Use a valid app-specific password (e.g. Gmail/Gmail SMTP app password, not the account password).","Confirm outbound SMTP is allowed from the Immich host/container (test with openssl s_client / swaks)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before sending, sanity-check SMTP connectivity\nimport net from 'net';\nawait new Promise<void>((resolve, reject) => {\n  const socket = net.createConnection({ host: smtp.host, port: smtp.port }, () => {\n    socket.end();\n    resolve();\n  });\n  socket.on('error', reject);\n  setTimeout(() => reject(new Error('SMTP connect timeout')), 5000);\n});","typeGuard":null,"tryCatchPattern":"try {\n  await adminApi.sendTestEmail(userId, smtpDto);\n} catch (e) {\n  if (e.status === 400 && /Failed to verify SMTP/.test(e.message)) {\n    // inspect e.cause for auth/TLS/connection detail and correct config\n  } else throw e;\n}","preventionTips":["Use the test-email flow to validate SMTP before enabling notifications.","Keep app-specific passwords for providers like Gmail; never the account password.","Confirm outbound SMTP port reachability from the Immich host."],"tags":["notification","email","smtp","config","network","bad-request","immich","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}