louislam/uptime-kuma · error · Error

TLS Connection failed: ${message}

Error message

TLS Connection failed: ${message}

What it means

Wraps every failure path inside checkTlsCertificate (tcp.js:236-279). The outer try/catch re-throws any underlying error — TLS handshake error, the 'Connection timed out' from socket.setTimeout (line 263), a checkCertificate failure, or the explicit 'Certificate is invalid' (line 269) — prefixed with 'TLS Connection failed: '. It exists so the monitor heartbeat surfaces one consistent error family regardless of which sub-step broke.

Source

Thrown at server/monitor-types/tcp.js:273

                    }
                });

                socket.on("error", (error) => {
                    reject(error);
                });

                socket.setTimeout(1000 * TIMEOUT, () => {
                    reject(new Error("Connection timed out"));
                });
            });

            await monitor.handleTlsInfo(tlsInfoObject);
            if (!tlsInfoObject.valid) {
                throw new Error("Certificate is invalid");
            }
        } catch (error) {
            const message = error instanceof Error ? error.message : "Unknown error";
            throw new Error(`TLS Connection failed: ${message}`);
        } finally {
            if (socket && !socket.destroyed) {
                socket.end();
            }
        }
    }

    /**
     * Check for expected TLS alert (for mTLS verification)
     * @param {object} monitor Monitor object
     * @param {object} heartbeat Heartbeat object
     * @param {string} expectedTlsAlert Expected TLS alert name
     * @returns {Promise<void>}
     */
    async checkTlsAlert(monitor, heartbeat, expectedTlsAlert) {
        const timeout = monitor.timeout * 1000 || 30000;
        const startTime = Date.now();

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Read the inner ${message} first — it pinpoints which sub-step failed (timeout vs invalid cert vs handshake error).
  2. If the message is 'Certificate is invalid' or names a cert problem, open the host in a browser or `openssl s_client -connect host:port -servername host` to inspect the chain/expiry.
  3. If the message is 'Connection timed out', verify network reachability and that monitor.hostname/port are correct.
  4. For self-signed/internal CAs, set the monitor's ignore-TLS option or supply the CA via the monitor's tls_ca field instead of disabling validation.
  5. Increase monitor.timeout if the host legitimately needs longer than TIMEOUT seconds to complete the handshake.

Example fix

// before: monitor points at host with self-signed cert and rejectUnauthorized on
// after: configure the monitor to trust the internal CA
monitor.tls_ca = fs.readFileSync('/etc/ssl/internal-ca.pem');
monitor.ignoreTls = false;
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight reachability + cert sanity before relying on checkTlsCertificate
const net = require('net');
function hostReachable(host, port, ms = 5000) {
    return new Promise((res) => {
        const s = net.createConnection({ host, port });
        s.setTimeout(ms);
        s.on('connect', () => { s.destroy(); res(true); });
        s.on('error', () => res(false));
        s.on('timeout', () => { s.destroy(); res(false); });
    });
}
if (!(await hostReachable(monitor.hostname, monitor.port))) {
    throw new Error(`Host unreachable: ${monitor.hostname}:${monitor.port}`);
}

Type guard

function isTlsInfoObject(v) {
    return v != null && typeof v === 'object'
        && typeof v.valid === 'boolean'
        && 'certInfo' in v;
}

Try / catch

try {
    await monitor.checkTlsCertificate(monitor);
} catch (e) {
    // unwrap: 'TLS Connection failed: <reason>'
    const reason = e.message.startsWith('TLS Connection failed:') ? e.message.slice('TLS Connection failed:'.length).trim() : e.message;
    heartbeat.status = reason.includes('timed out') ? PENDING : DOWN;
    heartbeat.msg = reason;
}

Prevention

When it happens

Trigger: Calling tls.connect against an unreachable/filtered host (timeout fires), a server whose certificate is self-signed/expired/chain-incomplete when rejectUnauthorized is on, a hostname mismatch (SNI vs cert CN/SAN), or a STARTTLS negotiation that the server refuses.

Common situations: Monitoring an internal service behind a self-signed cert without toggling 'ignore TLS', a renewed cert whose intermediate chain was not bundled, a hostname recently changed in DNS but the monitor still points at the old one, firewall egress blocking the TLS port so the 1.5xTIMEOUT socket timer fires.

Understand the failure class

Related errors


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