louislam/uptime-kuma · error · Error
Expected TLS alert '${expectedTlsAlert}' but connection succ
Error message
Expected TLS alert '${expectedTlsAlert}' but connection succeeded. The server accepted the connection without requiring a client certificate. What it means
Thrown by checkTlsAlert (tcp.js:288-337), a monitor mode that VERIFIES mTLS by asserting a server REJECTS an unauthenticated client with a specific RFC 8446 alert (e.g. 'certificate_required'). This branch fires when attemptTlsConnection resolved with success:true — the server accepted the connection instead of demanding a client certificate — so the security property the user is testing for is absent.
Source
Thrown at server/monitor-types/tcp.js:325
}
const result = await this.attemptTlsConnection(monitor, options, startTime, timeout);
heartbeat.ping = result.responseTime;
// Handle TLS info for certificate expiry monitoring
if (result.tlsInfo && monitor.isEnabledExpiryNotification()) {
await monitor.handleTlsInfo(result.tlsInfo);
}
// Check if we got the expected alert
// Note: Error messages below could be translated, but alert names (e.g., certificate_required)
// are from RFC 8446 spec and should remain in English for consistency with the spec.
if (result.alertName === expectedTlsAlert) {
heartbeat.status = UP;
heartbeat.msg = `TLS alert received as expected: ${result.alertName} (${result.alertNumber})`;
} else if (result.success) {
throw new Error(
`Expected TLS alert '${expectedTlsAlert}' but connection succeeded. The server accepted the connection without requiring a client certificate.`
);
} else if (result.alertNumber !== null) {
throw new Error(
`Expected TLS alert '${expectedTlsAlert}' but received '${result.alertName}' (${result.alertNumber})`
);
} else {
throw new Error(
`Expected TLS alert '${expectedTlsAlert}' but got unexpected error: ${result.errorMessage}`
);
}
}
/**
* Attempt TLS connection and capture result/alert
* @param {object} monitor Monitor object
* @param {object} options TLS connection options
* @param {number} startTime Connection start timestampView on GitHub (pinned to 6b5ea01557)
Solutions
- Confirm the target server actually requires client certificates (e.g. nginx `ssl_verify_client on;`, Apache `SSLVerifyClient require`).
- Verify the monitor is pointed at the port/listener that enforces mTLS, not a parallel open endpoint.
- Re-deploy or roll back the server config that disabled client-cert verification.
- If the server intentionally no longer requires client certs, remove or update the monitor's expected_tls_alert setting.
Example fix
// server side (nginx) — restore mTLS enforcement // ssl_client_certificate /etc/ssl/ca.pem; // ssl_verify_client on;
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the server actually requires client certs before relying on the alert check
const { execSync } = require('child_process');
function serverRequiresClientCert(host, port) {
try {
// Connect WITHOUT a client cert; openssl returns non-zero with 'alert' if required
execSync(`echo | openssl s_client -connect ${host}:${port} -servername ${host} 2>&1`, { stdio: 'pipe', timeout: 8000 });
return /alert.*certificate_required|alert.* handshake_failure/i.test(out);
} catch (e) {
return /alert/i.test(String(e.stdout || '') + e.message);
}
} Type guard
function isExpectedAlertConfigured(v) {
return typeof v === 'string' && v.length > 0 && /^[a-z_]+$/.test(v);
} Try / catch
try {
await monitor.checkTlsAlert(monitor, heartbeat, expectedTlsAlert);
} catch (e) {
if (/connection succeeded/i.test(e.message)) {
// server is NOT enforcing mTLS — escalate as a security finding, not a transient outage
heartbeat.status = DOWN;
heartbeat.msg = `mTLS NOT enforced: ${e.message}`;
} else { throw e; }
} Prevention
- Source expected_tls_alert from an observed run (openssl s_client output) rather than guessing.
- Periodically re-run the mTLS posture check after server config deploys.
When it happens
Trigger: Monitor configured with an expected_tls_alert (e.g. certificate_required) against a server whose TLS endpoint does not require client certs; the mTLS policy was removed or never enabled server-side; testing against the wrong port (a plain-HTTPS listener rather than the mTLS one).
Common situations: Security/compliance check expecting a service to enforce client certificates, but a reverse proxy was reconfigured to terminate TLS itself and forward plain HTTP upstream; the expected alert was set against a dev environment that has mTLS disabled; nginx env var SSL_VERIFY_CLIENT reset to 'off' during a deploy.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Expected TLS alert '${expectedTlsAlert}' but received '${res
- Certificate is invalid
- TLS Connection failed: ${message}
- Expected TLS alert '${expectedTlsAlert}' but got unexpected
- TLS certificate is not authorized: ${tlsInfo.error}
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/babbec90d10170f6.
Report an issue: GitHub.