louislam/uptime-kuma · warning · Error
Unexpected output: "${line}"
Error message
Unexpected output: "${line}" What it means
Thrown by parseTailscaleOutput() for any non-empty line that does not match the known 'pong from', 'timed out', 'no matching peer', or 'is local Tailscale IP' substrings. It is the fallback branch signalling that the installed tailscale CLI produced output the parser was not written to understand.
Source
Thrown at server/monitor-types/tailscale-ping.js:69
parseTailscaleOutput(tailscaleOutput, heartbeat) {
let lines = tailscaleOutput.split("\n");
for (let line of lines) {
if (line.includes("pong from")) {
heartbeat.status = UP;
let time = line.split(" in ")[1].split(" ")[0];
heartbeat.ping = parseInt(time);
heartbeat.msg = "OK";
break;
} else if (line.includes("timed out")) {
throw new Error(`Ping timed out: "${line}"`);
// Immediately throws upon "timed out" message, the server is expected to re-call the check function
} else if (line.includes("no matching peer")) {
throw new Error(`Nonexistant or inaccessible due to ACLs: "${line}"`);
} else if (line.includes("is local Tailscale IP")) {
throw new Error(`Tailscale only works if used on other machines: "${line}"`);
} else if (line !== "") {
throw new Error(`Unexpected output: "${line}"`);
}
}
}
}
module.exports = {
TailscalePing,
};
View on GitHub (pinned to 6b5ea01557)
Solutions
- Run 'tailscale ping --c 1 <hostname>' manually and compare the actual line text to the substrings the parser checks.
- If the wording changed, update parseTailscaleOutput() to recognise the new token (and keep the unexpected-output fallback).
- Suppress preface banners or ensure the environment locale is English (LC_ALL=C) for stable parsing.
- Pin the tailscale CLI version in deployments until the parser is updated.
Example fix
// before
} else if (line !== "") {
throw new Error(`Unexpected output: "${line}"`);
}
// after — extend recognised tokens before falling back
} else if (line.includes("via DERP")) {
// relayed pong, still a success-ish signal; handle accordingly
} else if (line !== "") {
throw new Error(`Unexpected output: "${line}"`);
} Defensive patterns
Strategy: validation
Validate before calling
const KNOWN_TOKENS = ["pong from", "timed out", "no matching peer", "is local Tailscale IP"];
function isKnownLine(line) { return line === "" || KNOWN_TOKENS.some(t => line.includes(t)); } Type guard
function isKnownLine(line) { return line === "" || KNOWN_TOKENS.some(t => line.includes(t)); } Try / catch
if (!isKnownLine(line)) { log.warn("tailscale", `Unrecognised line: ${line}`); heartbeat.status = DOWN; heartbeat.msg = `Unexpected output: "${line}"`; } Prevention
- Pin the tailscale CLI version until the parser recognises its output.
- Force LC_ALL=C so output phrasing stays stable.
- Extend KNOWN_TOKENS when a new tailscale version rewords status lines.
When it happens
Trigger: Produced when a newer (or older) tailscale version changes the ping output phrasing, adds a new status line, prints a warning banner, or emits locale-specific text that does not contain any recognised token.
Common situations: Tailscale CLI upgraded to a version that reworded status lines; a preface/warning (e.g. 'needs login', update notice) printed before the pong; non-English locale affecting output; experimental tailscale build with extra telemetry lines.
Related errors
- Error checking Tailscale ping: ${err}
- Error in output: ${res.stderr.toString()}
- No output from Tailscale ping
- Ping timed out: "${line}"
- Nonexistant or inaccessible due to ACLs: "${line}"
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/57dc0cb609fd61c1.
Report an issue: GitHub.