{"record":{"id":"906b11deac18551f","repo":"decolua/9router","slug":"tailscale-cert-provision-failed-non-fatal-e","errorCode":null,"errorMessage":"[Tailscale] cert provision failed (non-fatal): ${e.message}","messagePattern":"\\[Tailscale\\] cert provision failed \\(non-fatal\\): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/lib/tunnel/tailscale/tailscale.js","lineNumber":833,"sourceCode":"  });\n}\n\n/** Provision TLS cert for funnel domain (required before Funnel serves HTTPS). Best-effort. */\nexport async function provisionCert(hostname) {\n  const bin = getTailscaleBin();\n  if (!bin || !hostname) return;\n  const certsDir = path.join(TAILSCALE_DIR, \"certs\");\n  fs.mkdirSync(certsDir, { recursive: true });\n  const certFile = path.join(certsDir, `${hostname}.crt`);\n  const keyFile = path.join(certsDir, `${hostname}.key`);\n  try {\n    await execAsync(\n      `\"${bin}\" ${SOCKET_FLAG.join(\" \")} cert --cert-file \"${certFile}\" --key-file \"${keyFile}\" \"${hostname}\"`,\n      { windowsHide: true, env: { ...process.env, PATH: EXTENDED_PATH }, timeout: 30000 }\n    );\n    console.log(`[Tailscale] cert provisioned for ${hostname}`);\n  } catch (e) {\n    console.warn(`[Tailscale] cert provision failed (non-fatal): ${e.message}`);\n  }\n}\n\n/** Stop tailscale funnel */\nexport function stopFunnel() {\n  const bin = getTailscaleBin();\n  if (!bin) return;\n  try { execSync(`\"${bin}\" ${SOCKET_FLAG.join(\" \")} funnel --bg reset`, { stdio: \"ignore\", windowsHide: true }); } catch (e) { /* ignore */ }\n}\n\n/** Kill tailscaled daemon (runs as root, needs sudo) */\nexport async function stopDaemon(sudoPassword) {\n  // Try non-sudo first\n  try { execSync(\"pkill -x tailscaled\", { stdio: \"ignore\", windowsHide: true, timeout: 3000 }); } catch { /* ignore */ }\n\n  // Check if still alive\n  try { execSync(\"pgrep -x tailscaled\", { stdio: \"ignore\", windowsHide: true, timeout: 2000 }); } catch { return; } // Dead, done\n","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/tunnel/tailscale/tailscale.js#L815-L851","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ntry { await execAsync(cmd, { timeout: 30000 }); } catch (e) { console.warn(`[Tailscale] cert provision failed (non-fatal): ${e.message}`); }\n// after: pre-check tailscaled before attempting cert\nconst st = spawnSync(bin, [...SOCKET_FLAG, \"status\"], { encoding: \"utf8\" });\nif (st.status === 0) await execAsync(cmd, { timeout: 60000 });\nelse console.warn(\"[Tailscale] tailscaled not reachable, skipping cert\");","handlingStrategy":"validation","validationCode":"// pre-flight: tailscaled reachable and cert subcommand available\nimport { execFileSync } from \"child_process\";\ntry {\n  execFileSync(\"tailscale\", [\"status\"], { stdio: \"ignore\" });\n  execFileSync(\"tailscale\", [\"cert\", \"--help\"], { stdio: \"ignore\" });\n} catch (e) {\n  console.warn(\"tailscaled unreachable or old binary without cert support:\", e.message);\n}","typeGuard":null,"tryCatchPattern":"// cert failure is non-fatal upstream; plan for missing certs on your side\ntry {\n  await enableTailscale();\n} catch (e) {\n  if (/cert provision failed/.test(e.message)) console.warn(\"continuing without local TLS cert\");\n  else throw e;\n}","preventionTips":["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."],"tags":["tailscale","tls","certificate","funnel","subprocess"],"backgroundTag":"tls-cert-provisioning-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}