immich-app/immich · error · BadRequestException
Failed to verify SMTP configuration
Error message
Failed to verify SMTP configuration
What it means
sendTestEmail validates the SMTP transport by attempting a live verification (verifySmtp) before rendering and sending a test email. If the repository's SMTP check fails for any reason (bad host, port, credentials, TLS), the original error is wrapped in a BadRequestException with this message. It signals the stored/system email config cannot be used to send mail.
Solutions
- Verify SMTP host, port, username, and password in the notification settings; ensure the secure flag matches the port (465=secure, 587=starttls).
- Test connectivity from the server host (e.g. `nc -vz smtp.example.com 587`) to rule out firewall/DNS issues.
- If using Gmail or Office365, use an app password / SMTP AUTH with less-secure-app or OAuth settings.
- Inspect the `cause` of the BadRequestException in server logs for the underlying SMTP error detail.
Example fix
// before
await this.emailRepository.verifySmtp({ host: 'smtp.gmail.com', port: 587, secure: true });
// after
await this.emailRepository.verifySmtp({ host: 'smtp.gmail.com', port: 587, secure: false }); // 587 uses STARTTLS, not implicit TLS Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check config before calling
const ok = smtpHost && smtpPort > 0 && smtpPort < 65536 && (smtpUsername ? smtpPassword !== undefined : true);
if (!ok) throw new Error('Incomplete SMTP settings'); Try / catch
try {
await api.notificationsApi.sendTestEmail({ transport });
} catch (e) {
if (e.status === 400 && String(e.message).includes('Failed to verify SMTP configuration')) {
// surface e.cause / check host, port, credentials, TLS
} else throw e;
} Prevention
- Match the secure flag to the port (465 implicit TLS, 587 STARTTLS).
- Test SMTP credentials with a standalone client (swaks, msmtp) before wiring them in.
- Allowlist outbound SMTP ports on the server firewall.
- Use app passwords for providers like Gmail instead of the account password.
When it happens
Trigger: Calling POST /api/notifications/test-email (sendTestEmail) with a TestEmailDto whose transport (smtp host/port/username/password/secure settings) fails the verifySmtp check — connection refused, auth failure, or TLS handshake error.
Common situations: Wrong SMTP host or port (465 vs 587 mismatch with secure flag), incorrect username/password, self-signed certificates rejected by TLS, firewall blocking outbound SMTP, Gmail requiring app passwords.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Failed to verify SMTP configuration
- Email is not available
- Invalid SMTP configuration
- Asset does not have valid dimensions
- Asset not found
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/7e22376730343de8.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/notification.service.ts:268
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({
to: user.email,
subject: 'Test email from Immich',
html,
text,
from: dto.from,
replyTo: dto.replyTo || dto.from,View on GitHub (pinned to e55ac299a4)