{"record":{"id":"2c8c062506606a65","repo":"immich-app/immich","slug":"invalid-smtp-configuration","errorCode":null,"errorMessage":"Invalid SMTP configuration","messagePattern":"Invalid SMTP configuration","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/notification.service.ts","lineNumber":137,"sourceCode":"    this.websocketRepository.clientBroadcast('AppRestartV1', {\n      isMaintenanceMode: state.isMaintenanceMode,\n    });\n\n    this.websocketRepository.serverSend('AppRestart', state);\n  }\n\n  @OnEvent({ name: 'ConfigValidate', priority: -100 })\n  async onConfigValidate({ oldConfig, newConfig }: ArgOf<'ConfigValidate'>) {\n    try {\n      if (\n        newConfig.notifications.smtp.enabled &&\n        !isEqualObject(oldConfig.notifications.smtp, newConfig.notifications.smtp)\n      ) {\n        await this.emailRepository.verifySmtp(newConfig.notifications.smtp.transport);\n      }\n    } catch (error: Error | any) {\n      this.logger.error(`Failed to validate SMTP configuration: ${error}`, error?.stack);\n      throw new Error('Invalid SMTP configuration', { cause: error });\n    }\n  }\n\n  @OnEvent({ name: 'AssetHide' })\n  onAssetHide({ assetId, userId }: ArgOf<'AssetHide'>) {\n    this.websocketRepository.clientSend('on_asset_hidden', userId, assetId);\n  }\n\n  @OnEvent({ name: 'AssetShow' })\n  async onAssetShow({ assetId }: ArgOf<'AssetShow'>) {\n    await this.jobRepository.queue({ name: JobName.AssetGenerateThumbnails, data: { id: assetId, notify: true } });\n  }\n\n  @OnEvent({ name: 'AssetTrash' })\n  onAssetTrash({ assetId, userId }: ArgOf<'AssetTrash'>) {\n    this.websocketRepository.clientSend('on_asset_trash', userId, [assetId]);\n  }\n","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/notification.service.ts#L119-L155","documentation":"On the ConfigValidate event, NotificationService verifies the SMTP transport whenever notifications.smtp is enabled and has changed. If verifySmtp throws, the catch logs the error and rethrows a plain Error 'Invalid SMTP configuration' with the cause. Because this fires during config save validation, a failure blocks the new system config from being persisted.","triggerScenarios":"Admin PATCHes /system-config with notifications.smtp.enabled=true and a changed transport that fails verification (unreachable host, bad credentials, TLS problem). The ConfigValidate handler runs and throws before the save completes.","commonSituations":"Enabling SMTP notifications for the first time with unverified settings; changing SMTP host/port/password; switching security modes incorrectly; SMTP provider outage during save.","solutions":["Read error.cause and the 'Failed to validate SMTP configuration' log for the underlying SMTP error.","Correct host, port, username, password, and security (SSL vs STARTTLS) in the SMTP config payload.","Use the admin test-email flow first to verify credentials before saving system config.","Ensure network/firewall allows the Immich server outbound to the SMTP host:port."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify SMTP before saving system config\nimport nodemailer from 'nodemailer';\nconst transporter = nodemailer.createTransport(newConfig.notifications.smtp.transport);\nawait transporter.verify(); // throws on bad config\nawait api.systemConfigApi.updateConfig(newConfig);","typeGuard":null,"tryCatchPattern":"try {\n  await api.systemConfigApi.updateConfig(newConfig);\n} catch (e) {\n  if (/Invalid SMTP configuration/.test(String(e?.message))) {\n    // read e.cause - correct host/port/credentials/security before retry\n  } else throw e;\n}","preventionTips":["Run the admin test-email flow before enabling notifications.smtp.","Verify SMTP transport (nodemailer.verify) in a staging env first.","Ensure network egress to the SMTP host is allowed."],"tags":["notification","email","smtp","config","validation","network","immich","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}