decolua/9router · error · Error

Tailscale not installed

Error message

Tailscale not installed

What it means

startFunnel(port) needs the tailscale CLI binary and resolves it via getTailscaleBin(); when the binary can't be found on the system it throws 'Tailscale not installed'. The tunnel layer refuses to attempt `tailscale funnel` without the executable, so the failure is immediate and deterministic.

Source

Thrown at src/lib/tunnel/tailscale/tailscale.js:743

        return;
      }
      // Only resolve alreadyLoggedIn if status confirms BackendState=Running
      if (isTailscaleLoggedIn()) {
        resolved = true;
        clearTimeout(timeout);
        clearInterval(statusPoll);
        resolve({ alreadyLoggedIn: true });
        return;
      }
      // Otherwise keep polling — daemon may publish AuthURL shortly after exit
    });
  });
}

/** Start tailscale funnel for the given port */
export async function startFunnel(port) {
  const bin = getTailscaleBin();
  if (!bin) throw new Error("Tailscale not installed");

  // Reset any existing funnel
  try { execSync(`"${bin}" ${SOCKET_FLAG.join(" ")} funnel --bg reset`, { stdio: "ignore", windowsHide: true }); } catch (e) { /* ignore */ }

  return new Promise((resolve, reject) => {
    const child = spawn(bin, tsArgs("funnel", "--bg", `${port}`), {
      stdio: ["ignore", "pipe", "pipe"],
      windowsHide: true
    });

    let resolved = false;
    let output = "";

    const timeout = setTimeout(() => {
      if (resolved) return;
      resolved = true;
      // --bg exits after setup, read actual hostname from status
      const url = getActualFunnelUrl() || getTailscaleFunnelUrl(port);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Install tailscale (official installer, `winget install tailscale.tailscale`, brew, or the Linux install script) before enabling the funnel.
  2. Confirm `tailscale version` works in the same shell/environment the gateway runs in; if not, fix PATH (e.g. symlink into /usr/local/bin).
  3. Check what getTailscaleBin() searches and point the environment (PATH) at your actual install location.
  4. Restart the gateway process after installing tailscale so binary discovery re-runs.
  5. Use the built-in install flow (installTailscale) instead of expecting a pre-installed binary.

Example fix

// before
await startFunnel(20128);
// after
import { getTailscaleBin } from ".../tailscale.js";
if (!getTailscaleBin()) {
  await installTailscale(); // or instruct user to install tailscale
}
await startFunnel(20128);
Defensive patterns

Strategy: fallback

Validate before calling

import { execSync } from "child_process";
function tailscaleAvailable() {
  try { execSync("tailscale version", { stdio: "ignore" }); return true; }
  catch { return false; }
}
if (!tailscaleAvailable()) console.warn("tailscale missing — install before enabling funnel");

Type guard

const hasTailscale = (bin) => typeof bin === "string" && bin.length > 0;

Try / catch

try {
  await startFunnel(port);
} catch (e) {
  if (e.message === "Tailscale not installed") {
    await installTailscale();      // fallback: auto-install
    return startFunnel(port);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling startFunnel (or the enableTailscale flow that reaches it) on a machine where tailscale was never installed, was uninstalled, is installed but not on PATH, or where the platform-specific bin lookup (getTailscaleBin) checks a location that doesn't match the actual install (e.g. per-user vs per-machine Windows install, Homebrew vs /usr/local on macOS).

Common situations: Fresh machine or container without tailscale; PATH missing the tailscale dir in the server's environment (systemd/daemon environments often have a minimal PATH); tailscale installed after the gateway process started with a cached lookup; Windows install to a non-default directory.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/6d248258756b4dc1. Report an issue: GitHub.