louislam/uptime-kuma · error · Error

SMTP connection doesn't verify: ${e}

Error message

SMTP connection doesn't verify: ${e}

What it means

The SMTP monitor builds a nodemailer transport from monitor.hostname, port, and smtpSecurity mode, then calls transporter.verify() to open and authenticate a connection. Any failure (socket, TLS, auth) is caught and re-thrown as 'SMTP connection doesn\'t verify: <e>'. The original nodemailer error is interpolated, so the message after the colon carries the real cause.

Source

Thrown at server/monitor-types/smtp.js:26

    /**
     * @inheritdoc
     */
    async check(monitor, heartbeat, _server) {
        let options = {
            port: monitor.port || 25,
            host: monitor.hostname,
            secure: monitor.smtpSecurity === "secure", // use SMTPS (not STARTTLS)
            ignoreTLS: monitor.smtpSecurity === "nostarttls", // don't use STARTTLS even if it's available
            requireTLS: monitor.smtpSecurity === "starttls", // use STARTTLS or fail
        };
        let transporter = nodemailer.createTransport(options);
        try {
            await transporter.verify();

            heartbeat.status = UP;
            heartbeat.msg = "SMTP connection verifies successfully";
        } catch (e) {
            throw new Error(`SMTP connection doesn't verify: ${e}`);
        } finally {
            transporter.close();
        }
    }
}

module.exports = {
    SMTPMonitorType,
};

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Match the port to smtpSecurity: 465 for 'secure' (SMTPS), 587 for 'starttls', 25 for plain/'nostarttls'.
  2. Confirm outbound connectivity from the Uptime Kuma host: openssl s_client -connect host:465 or telnet host 587.
  3. Verify credentials and that the user is permitted to relay/auth.
  4. If STARTTLS is required, ensure the server advertises it and the cert is valid.

Example fix

// before
monitor.smtpSecurity = 'secure';   // but port 587
// after
monitor.smtpSecurity = 'starttls';  // matches port 587
Defensive patterns

Strategy: try-catch

Validate before calling

const net = require('net');
function preflightSmtp(host, port, security) {
  // sanity: port matches declared security mode
  const ok = (security === 'secure' && port === 465) ||
             (security === 'starttls' && port === 587) ||
             (security === 'nostarttls' && port === 25);
  if (!ok) console.warn(`Port ${port} may not match SMTP security mode '${security}'`);
}

Type guard

function isSmtpPortModeMatch(port, security) {
  return (security === 'secure' && port === 465) ||
         (security === 'starttls' && port === 587) ||
         (security === 'nostarttls' && port === 25);
}

Try / catch

try {
  await smtpMonitor.check(monitor, heartbeat, server);
} catch (e) {
  if (/doesn't verify/.test(e.message)) {
    // inner cause is after the colon: GREETING/EAUTH/ECONNECTION/EENVELOPE
    heartbeat.status = DOWN;
    heartbeat.msg = e.message;
  }
}

Prevention

When it happens

Trigger: transporter.verify() rejects: host unreachable, connection refused, TLS handshake failure, STARTTLS required but unavailable, or authentication error. The finally block still calls transporter.close().

Common situations: Wrong port (25/465/587 mismatch with smtpSecurity), secure vs STARTTLS mismatch, firewall blocking outbound SMTP, credentials wrong, or host doing opportunistic TLS that fails.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/32eb11b32a2358a5. Report an issue: GitHub.