decolua/9router · warning

[Tailscale] cert provision failed (non-fatal): ${e.message}

Error message

[Tailscale] cert provision failed (non-fatal): ${e.message}

What it means

The tailscale cert provisioning step runs 'tailscale cert --cert-file ... --key-file ... <hostname>' to mint TLS certs for the funnel. If this exec fails or times out (30s), the failure is treated as non-fatal: the warning is logged and funnel setup continues without the cert. Funnel may fall back to plain HTTP or Tailscale's automatic cert management.

Source

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

  });
}

/** Provision TLS cert for funnel domain (required before Funnel serves HTTPS). Best-effort. */
export async function provisionCert(hostname) {
  const bin = getTailscaleBin();
  if (!bin || !hostname) return;
  const certsDir = path.join(TAILSCALE_DIR, "certs");
  fs.mkdirSync(certsDir, { recursive: true });
  const certFile = path.join(certsDir, `${hostname}.crt`);
  const keyFile = path.join(certsDir, `${hostname}.key`);
  try {
    await execAsync(
      `"${bin}" ${SOCKET_FLAG.join(" ")} cert --cert-file "${certFile}" --key-file "${keyFile}" "${hostname}"`,
      { windowsHide: true, env: { ...process.env, PATH: EXTENDED_PATH }, timeout: 30000 }
    );
    console.log(`[Tailscale] cert provisioned for ${hostname}`);
  } catch (e) {
    console.warn(`[Tailscale] cert provision failed (non-fatal): ${e.message}`);
  }
}

/** Stop tailscale funnel */
export function stopFunnel() {
  const bin = getTailscaleBin();
  if (!bin) return;
  try { execSync(`"${bin}" ${SOCKET_FLAG.join(" ")} funnel --bg reset`, { stdio: "ignore", windowsHide: true }); } catch (e) { /* ignore */ }
}

/** Kill tailscaled daemon (runs as root, needs sudo) */
export async function stopDaemon(sudoPassword) {
  // Try non-sudo first
  try { execSync("pkill -x tailscaled", { stdio: "ignore", windowsHide: true, timeout: 3000 }); } catch { /* ignore */ }

  // Check if still alive
  try { execSync("pgrep -x tailscaled", { stdio: "ignore", windowsHide: true, timeout: 2000 }); } catch { return; } // Dead, done

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Run `tailscale cert --cert-file <cert> --key-file <key> <hostname>` manually to see the real error.
  2. Ensure tailscaled is running and `tailscale status` works with the same socket (SOCKET_FLAG) used by the app.
  3. Update the tailscale binary to a version supporting `cert` and confirm the funnel hostname matches your tailnet DNS name.
  4. Increase patience/timeout (cert issuance can be slow) and verify write permission for the cert/key paths, then re-enable the funnel.

Example fix

// before
try { await execAsync(cmd, { timeout: 30000 }); } catch (e) { console.warn(`[Tailscale] cert provision failed (non-fatal): ${e.message}`); }
// after: pre-check tailscaled before attempting cert
const st = spawnSync(bin, [...SOCKET_FLAG, "status"], { encoding: "utf8" });
if (st.status === 0) await execAsync(cmd, { timeout: 60000 });
else console.warn("[Tailscale] tailscaled not reachable, skipping cert");
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: tailscaled reachable and cert subcommand available
import { execFileSync } from "child_process";
try {
  execFileSync("tailscale", ["status"], { stdio: "ignore" });
  execFileSync("tailscale", ["cert", "--help"], { stdio: "ignore" });
} catch (e) {
  console.warn("tailscaled unreachable or old binary without cert support:", e.message);
}

Try / catch

// cert failure is non-fatal upstream; plan for missing certs on your side
try {
  await enableTailscale();
} catch (e) {
  if (/cert provision failed/.test(e.message)) console.warn("continuing without local TLS cert");
  else throw e;
}

Prevention

When it happens

Trigger: The tailscale CLI cert command exits non-zero or exceeds the 30s timeout: tailscaled not running/unreachable, hostname not a valid tailnet DNS name, cert API slow, EACCES on cert/key output paths, tailscale binary lacking cert subcommand (old version).

Common situations: tailscaled restarted or blocked (Linux socket permission, Windows service stopped); self-signed/DNS-name mismatch; slow first-time cert issuance via Let's Encrypt; tailscale version predating 'tailscale cert'; antivirus blocking file writes.

Related errors


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