immich-app/immich · error · BadRequestException
Failed to verify SMTP configuration
Error message
Failed to verify SMTP configuration
What it means
Thrown by NotificationAdminService.sendTestEmail when the SMTP transport supplied in the test-email request fails verifySmtp, meaning Immich could not establish/authenticate an SMTP session with the provided configuration. The original transport error is attached as the exception cause. The test email is aborted before rendering.
Solutions
- Read the `cause` of the error for the underlying SMTP failure (auth failed, connection refused, TLS error) and fix that specific issue
- Confirm host, port, and security mode match your provider (e.g. 465 = TLS, 587 = STARTTLS)
- Test credentials by logging into the mail provider; use an app password if 2FA is enabled
- Verify the Immich server container can reach the SMTP host (network/firewall/DNS)
Example fix
// before
{ "transport": { "host": "smtp.gmail.com", "port": 587, "secure": true } } // secure:true on 587 fails handshake
// after
{ "transport": { "host": "smtp.gmail.com", "port": 587, "secure": false } } // STARTTLS on submission port Defensive patterns
Strategy: try-catch
Validate before calling
const net = require('net');
const reachable = await new Promise(res => {
const s = net.connect(transport.port, transport.host);
s.once('connect', () => { s.end(); res(true); });
s.once('error', () => res(false));
s.setTimeout(5000, () => { s.destroy(); res(false); });
});
if (!reachable) throw new Error(`Cannot reach SMTP host ${transport.host}:${transport.port}`); Type guard
function hasValidSmtpTransport(t) {
return !!t && typeof t.host === 'string' && t.host.length > 0 && Number.isInteger(t.port) && t.port > 0;
} Try / catch
try {
await api.notificationsApi.sendTestEmail(transport);
} catch (e) {
if (String(e.message).includes('Failed to verify SMTP configuration')) {
console.error('SMTP verify failed:', e.cause ?? e.response?.data); // inspect underlying cause
} else throw e;
} Prevention
- Match security mode to port (465 = implicit TLS, 587 = STARTTLS)
- Verify credentials with the mail provider and use app passwords where 2FA is on
- Test DNS/firewall reachability of the SMTP host from the server container
- Read the error's cause field to pinpoint auth vs connection vs TLS failures
When it happens
Trigger: Calling the admin test-email endpoint (POST /notifications/admin/test-email) with dto.transport whose host/port/credentials are wrong, the server is unreachable, TLS settings mismatch, or authentication fails during verifySmtp.
Common situations: Typo in SMTP host or port; using port 465 with STARTTLS (or 587 with implicit TLS) so the handshake fails; wrong username/password or OAuth settings; corporate firewall blocking outbound SMTP; mail provider requiring an app password.
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/5d6cb3ddbc75574c.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/notification-admin.service.ts:34
level: dto.level ?? NotificationLevel.Info,
title: dto.title,
description: dto.description,
data: dto.data,
});
return mapNotification(item);
}
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)