louislam/uptime-kuma · error · Error

No output from Tailscale ping

Error message

No output from Tailscale ping

What it means

Thrown by runTailscalePing() when the spawn resolved with neither a stderr error (code 0 or empty stderr) nor any stdout. The command produced no parseable output at all, so there is nothing to feed to parseTailscaleOutput().

Source

Thrown at server/monitor-types/tailscale-ping.js:40

     * Runs the Tailscale ping command to the given URL.
     * @param {string} hostname The hostname to ping.
     * @param {number} interval Interval to send ping
     * @returns {Promise<string>} A Promise that resolves to the output of the Tailscale ping command
     * @throws Will throw an error if the command execution encounters any error.
     */
    async runTailscalePing(hostname, interval) {
        let timeout = interval * 1000 * 0.8;
        let res = await childProcessAsync.spawn("tailscale", ["ping", "--c", "1", hostname], {
            timeout: timeout,
            encoding: "utf8",
        });
        if (res.stderr && res.stderr.toString() && res.code !== 0) {
            throw new Error(`Error in output: ${res.stderr.toString()}`);
        }
        if (res.stdout && res.stdout.toString()) {
            return res.stdout.toString();
        } else {
            throw new Error("No output from Tailscale ping");
        }
    }

    /**
     * Parses the output of the Tailscale ping command to update the heartbeat.
     * @param {string} tailscaleOutput The output of the Tailscale ping command.
     * @param {object} heartbeat The heartbeat object to update.
     * @returns {void}
     * @throws Will throw an eror if the output contains any unexpected string.
     */
    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);

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Run the same 'tailscale ping --c 1 <hostname>' command manually and confirm it actually prints to stdout.
  2. Check the monitor interval — a very small interval yields a tiny timeout; raise it so the command has time to emit output.
  3. Restart tailscaled if it is in an odd state producing no output.
  4. Inspect whether a shell alias or container entrypoint is redirecting stdout.
Defensive patterns

Strategy: validation

Validate before calling

function hasStdout(res) { return !!res?.stdout && res.stdout.toString().trim().length > 0; }

Type guard

function hasStdout(res) { return !!res?.stdout && res.stdout.toString().trim().length > 0; }

Try / catch

if (!hasStdout(res)) { heartbeat.status = DOWN; heartbeat.msg = "No output from Tailscale ping"; return; }

Prevention

When it happens

Trigger: Produced when 'tailscale ping' exits successfully (code 0) but writes nothing to stdout, or when stdout is empty/whitespace-only. Can happen with abnormal daemon states, SIGPIPE, or a successful exit before any line is flushed.

Common situations: Daemon in an inconsistent state returning immediately; very short timeout (interval * 0.8) cutting the process off before output flushes; tailscale version that writes everything to stderr with code 0; a wrapper/alias swallowing stdout in the deployment.

Related errors


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