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

  1. Run 'tailscale ping --c 1 <hostname>' manually and compare the actual line text to the substrings the parser checks.
  2. If the wording changed, update parseTailscaleOutput() to recognise the new token (and keep the unexpected-output fallback).
  3. Suppress preface banners or ensure the environment locale is English (LC_ALL=C) for stable parsing.
  4. 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

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


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