louislam/uptime-kuma · error · Error
${message}
Error message
${message} What it means
websocket-upgrade.js:46-47. attemptUpgrade returned a message but NO close code (code === undefined), meaning the connection never got far enough to negotiate a normal close — only an error event with a textual reason. The raw message is re-thrown verbatim.
Source
Thrown at server/monitor-types/websocket-upgrade.js:47
* @inheritdoc
*/
async check(monitor, heartbeat, _server) {
const [message, code] = await this.attemptUpgrade(monitor);
if (typeof code !== "undefined") {
// If returned status code matches user controlled accepted status code(default 1000), return success
if (checkStatusCode(code, JSON.parse(monitor.accepted_statuscodes_json))) {
heartbeat.status = UP;
heartbeat.msg = message;
return; // success at this point
}
// Throw an error using friendly name if defined, fallback to generic msg
throw new Error(WS_ERR_CODE[code] || `Unexpected status code: ${code}`);
}
// If no close code, then an error has occurred, display to user
if (typeof message !== "undefined") {
throw new Error(`${message}`);
}
// Throw generic error if nothing is defined, should never happen
throw new Error("Unknown Websocket Error");
}
/**
* Builds the WebSocket options object for authentication and TLS.
* Supports basic auth, OAuth2 client credentials, and mTLS.
* @param {object} monitor The monitor object for input parameters.
* @returns {Promise<object>} The options object to pass to the WebSocket constructor.
*/
async buildWsOptions(monitor) {
const options = {};
const timeoutMs = (monitor.timeout ?? 20) * 1000;
options.handshakeTimeout = timeoutMs;
// Parse custom headers if providedView on GitHub (pinned to 6b5ea01557)
Solutions
- Read the message verbatim — it usually names the real cause (e.g. 'Unexpected server response: 401', 'self signed certificate').
- Verify the URL scheme matches the endpoint (wss:// for TLS).
- For auth-related messages, check basic/bearer/oauth credentials in buildWsOptions output.
- For TLS messages, set ignoreTls or supply the correct CA.
Example fix
// before: ws://endpoint requiring TLS → 'Unexpected server response' // after: use the TLS endpoint monitor.url = monitor.url.replace(/^ws:/, 'wss:');
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate URL scheme/auth before attempting the upgrade
function validateWsTarget(url) {
const u = new URL(url);
if (!/^wss?:$/.test(u.protocol)) throw new Error(`Bad scheme: ${u.protocol}`);
return u;
} Type guard
function isWsUpgradeResult(v) {
return Array.isArray(v) && v.length === 2
&& (typeof v[0] === 'string' || v[0] == null)
&& (typeof v[1] === 'number' || v[1] == null);
} Try / catch
try {
await monitor.check(monitor, heartbeat, server);
} catch (e) {
if (/Unexpected server response: (401|403)/.test(e.message)) {
heartbeat.msg = `WS auth rejected: ${e.message}`;
} else if (/certificate|self-signed/i.test(e.message)) {
heartbeat.msg = `WS TLS problem: ${e.message}`;
}
heartbeat.status = DOWN;
} Prevention
- Use wss:// for TLS endpoints and pre-validate credentials.
- Keep ignoreTls/CA configuration in sync with the equivalent HTTP monitor.
When it happens
Trigger: TLS handshake failure on wss://, DNS resolution error, ECONNREFUSED, invalid WebSocket URL scheme, or an upgrade rejection that surfaces as an 'error' event with a message but no close frame.
Common situations: Wrong URL scheme (ws:// against a TLS-only port), expired/invalid server cert, network egress blocked, ws library emitting 'Unexpected server response: 401' when auth headers are wrong.
Related errors
- SMTP connection doesn't verify: ${e}
- TLS Connection failed: ${message}
- Expected TLS alert '${expectedTlsAlert}' but got unexpected
- ${WS_ERR_CODE[code] || `Unexpected status code: ${code}`}
- Invalid Docker response, is it Docker really a daemon?
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/fbaa926cab4de962.
Report an issue: GitHub.