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
- Run `tailscale cert --cert-file <cert> --key-file <key> <hostname>` manually to see the real error.
- Ensure tailscaled is running and `tailscale status` works with the same socket (SOCKET_FLAG) used by the app.
- Update the tailscale binary to a version supporting `cert` and confirm the funnel hostname matches your tailnet DNS name.
- 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
- Keep the tailscale binary current (`tailscale cert` requires newer versions).
- Confirm tailscaled is running with the same socket path the app uses.
- Ensure write permission on the cert/key output directory.
- Expect slow first cert issuance; avoid tight timeouts on flaky networks.
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
- Sudo password required to install Root CA certificate
- Failed to trust certificate: ${e.message}
- [Tailscale] health check timed out, will retry via watchdog
- cancelled
- Health check timeout after ${HEALTH_CHECK.timeoutMs}ms
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/906b11deac18551f.
Report an issue: GitHub.