louislam/uptime-kuma · error · Error

Error checking Tailscale ping: ${err}

Error message

Error checking Tailscale ping: ${err}

What it means

Top-level catch-all inside TailscalePing.check(). It wraps any error thrown by runTailscalePing() (spawn failure, stderr output, empty output) or parseTailscaleOutput() (timed out, ACL, unexpected line) into a single 'Error checking Tailscale ping' message that carries the original cause in its string. The original error is concatenated rather than chained via .cause, so structured detail is lost.

Source

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

const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");
const childProcessAsync = require("promisify-child-process");

class TailscalePing extends MonitorType {
    name = "tailscale-ping";

    /**
     * @inheritdoc
     */
    async check(monitor, heartbeat, _server) {
        try {
            let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval);
            this.parseTailscaleOutput(tailscaleOutput, heartbeat);
        } catch (err) {
            // trigger log function somewhere to display a notification or alert to the user (but how?)
            throw new Error(`Error checking Tailscale ping: ${err}`);
        }
    }

    /**
     * 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()}`);

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Read the inner error text to identify which specific failure occurred (ENOENT, timeout, 'no matching peer', etc.) and address that root cause.
  2. Ensure the 'tailscale' binary is installed and on PATH for the Uptime-Kuma process.
  3. Confirm tailscaled is running and logged in: 'tailscale status'.
  4. If migrating to structured errors, wrap with `new Error(msg, { cause: err })` so the original is inspectable.

Example fix

// before
} catch (err) {
    throw new Error(`Error checking Tailscale ping: ${err}`);
}

// after — preserve the cause chain for diagnostics
} catch (err) {
    throw new Error(`Error checking Tailscale ping: ${err.message}`, { cause: err });
}
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require("fs");
function tailscaleAvailable() {
  try { require("child_process").execFileSync("tailscale", ["--version"], { stdio: "ignore" }); return true; }
  catch { return false; }
}

Type guard

function isTailscaleError(e) { return /Tailscale/.test(e?.message || ""); }

Try / catch

try { ... }
catch (err) {
  heartbeat.status = DOWN;
  heartbeat.msg = `Error checking Tailscale ping: ${err.message}`;
  // throw new Error(..., { cause: err }) if re-throwing
}

Prevention

When it happens

Trigger: Produced for ANY failure during a tailscale-ping monitor cycle: 'tailscale' binary not found, spawn ENOENT, command timeout, non-zero exit with stderr, empty stdout, or any unrecognised line in the parsed output.

Common situations: Tailscale CLI not installed on the Uptime-Kuma host; tailscaled not running or not logged in; ACLs blocking the target; the monitor hostname is the machine's own Tailscale IP; the target node is offline; the installed tailscale version emits output the parser does not recognise.

Related errors


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