louislam/uptime-kuma · error · Error
Expected TLS alert '${expectedTlsAlert}' but received '${res
Error message
Expected TLS alert '${expectedTlsAlert}' but received '${result.alertName}' (${result.alertNumber}) What it means
Sibling branch in checkTlsAlert (tcp.js:328-331). attemptTlsConnection failed (success:false) and parseTlsAlertNumber extracted a numeric alert, but its name does not match the expected_tls_alert the user configured. So the server DID send a TLS alert — just not the one the monitor asserts should appear.
Source
Thrown at server/monitor-types/tcp.js:329
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 timestamp
* @param {number} timeout Connection timeout in ms
* @returns {Promise<object>} Connection result with success, responseTime, tlsInfo, alertNumber, alertName, errorMessage
*/
attemptTlsConnection(monitor, options, startTime, timeout) {View on GitHub (pinned to 6b5ea01557)
Solutions
- Compare result.alertName from the heartbeat with expectedTlsAlert — they must match exactly (RFC 8446 snake_case names).
- Update the monitor's expected_tls_alert to the alert the server now legitimately returns, OR fix the server so it returns the expected one.
- For 'bad_certificate' alerts, supply a valid client cert/key in the monitor (tls_cert/tls_key) so the test reflects the intended scenario.
- Run `openssl s_client -connect host:port` locally to see the alert the server emits.
Example fix
// before: monitor.expected_tls_alert = 'certificate_required' but server sends handshake_failure // after: align expectation with observed behaviour monitor.expected_tls_alert = 'handshake_failure';
Defensive patterns
Strategy: validation
Validate before calling
// Capture the real alert the server emits, then align configuration
function captureServerAlert(host, port) {
const out = require('child_process').execSync(
`openssl s_client -connect ${host}:${port} -servername ${host} -tlsextdebug 2>&1 < /dev/null`,
{ encoding: 'utf8', timeout: 8000 }
);
const m = out.match(/alert\s+(\d+)[^:]*:\s*([A-Za-z_]+)/);
return m ? { number: +m[1], name: m[2] } : null;
} Type guard
function isAlertResult(v) {
return v != null && typeof v === 'object'
&& typeof v.alertNumber === 'number'
&& typeof v.alertName === 'string';
} Try / catch
try {
await monitor.checkTlsAlert(monitor, heartbeat, expectedTlsAlert);
} catch (e) {
if (/but received/.test(e.message)) {
// extract the received alert and reconcile config or server
const received = e.message.match(/received '([a-z_]+)'\s*\((\d+)\)/);
log.warn(`Alert mismatch — expected ${expectedTlsAlert}, got ${received?.[1]} (${received?.[2]})`);
}
throw e;
} Prevention
- Keep a mapping of RFC 8446 alert numbers to names next to your runbook.
- Treat an unexpected alert as a signal to diff server TLS config, not just a monitor failure.
When it happens
Trigger: Server returns 'handshake_failure' (40) when the user expected 'certificate_required' (116); client offered a cert the server rejected with 'bad_certificate' (10); protocol/cipher mismatch yields 'illegal_parameter' (47) instead of an auth alert.
Common situations: The expected_tls_alert was guessed rather than taken from an observed run; server-side TLS policy changed to reject for a different reason; client cert present but expired prompts 'certificate_expired' instead of 'certificate_required'.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Expected TLS alert '${expectedTlsAlert}' but connection succ
- TLS Connection failed: ${message}
- Expected TLS alert '${expectedTlsAlert}' but got unexpected
- TLS certificate is not authorized: ${tlsInfo.error}
- Certificate is invalid
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/868f854f11ab9850.
Report an issue: GitHub.