decolua/9router · error · Error

Failed to trust certificate: ${e.message}

Error message

Failed to trust certificate: ${e.message}

What it means

This wraps any failure from installCert(password, rootCACertPath) — the platform-specific routine that adds the 9Router Root CA to the OS trust store (security/updates settings on macOS, certutil/registry on Windows, update-ca-certificates equivalents on Linux). The original error message is preserved in the template so the root cause (command missing, permission denied, timeout, keychain rejection) surfaces as 'Failed to trust certificate: <cause>'.

Source

Thrown at src/mitm/manager.js:567

  // Step 1.5: Auto-install Root CA if not trusted yet
  const { checkCertInstalled } = require("./cert/install");
  const rootCATrusted = await checkCertInstalled(rootCACertPath);
  const linuxNoSystemTrust = !IS_WIN && !IS_MAC && !isSudoAvailable();
  if (!rootCATrusted) {
    log("🔐 Cert: not trusted → installing...");
    const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
    if (linuxNoSystemTrust) {
      log(`🔐 Cert: skipping system trust (no sudo). Install ${rootCACertPath} as a trusted CA on machines that use this proxy.`);
    } else {
      if (!password && isSudoPasswordRequired()) {
        throw new Error("Sudo password required to install Root CA certificate");
      }
      try {
        await installCert(password, rootCACertPath);
        log("🔐 Cert: ✅ trusted");
      } catch (e) {
        throw new Error(`Failed to trust certificate: ${e.message}`);
      }
    }
  } else {
    log("🔐 Cert: already trusted ✅");
  }

  // Step 2: Spawn server (Root CA already installed in Step 1.5)
  // Verify server.js exists — recopy if runtime file was deleted (antivirus/cleanup)
  let effectiveServerPath = SERVER_PATH;
  if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {
    log(`[MITM] server.js missing at ${effectiveServerPath} → recopying`);
    effectiveServerPath = ensureRuntimeServer(resolveBundledServerPath());
    if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {
      throw new Error(`MITM server.js not found at ${effectiveServerPath}. Reinstall 9router.`);
    }
  }
  const mitmRouterBase = await resolveMitmRouterBaseUrl();
  log(`🚀 Starting server... (router: ${mitmRouterBase})`);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Read the underlying cause in the message and fix accordingly (wrong password → re-save it; missing binary → install the ca-certificates package)
  2. Manually trust the CA: `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/.9router/mitm/rootCA.crt` (macOS) or `sudo cp rootCA.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates` (Debian/Ubuntu), then restart — checkCertInstalled will skip install
  3. Regenerate the CA if the file is corrupt/expired: delete rootCA.crt/rootCA.key under the MITM_DIR so startServer regenerates them
  4. Use the linuxNoSystemTrust mode (run without sudo availability on Linux) and distribute the CA to client machines yourself

Example fix

// before: opaque failure at runtime
await startServer(apiKey, password);
// after: pre-trust manually so installCert is skipped
execSync(`sudo cp ${mitmDir}/rootCA.crt /usr/local/share/ca-certificates/9router.crt && sudo update-ca-certificates`);
await startServer(apiKey);
Defensive patterns

Strategy: fallback

Validate before calling

const fs = require('fs');
const certPath = require('os').homedir() + '/.9router/mitm/rootCA.crt';
function caFilesLookValid() {
  try {
    const pem = fs.readFileSync(certPath, 'utf-8');
    return pem.includes('BEGIN CERTIFICATE') && pem.includes('END CERTIFICATE');
  } catch { return false; }
}

Try / catch

try {
  await startServer(apiKey, password);
} catch (e) {
  const m = /Failed to trust certificate: (.+)/.exec(e.message);
  if (m) {
    console.error('CA install failed:', m[1]);
    // fallback: install the CA manually with the platform tool, then retry
    execSync(`sudo cp ${certPath} /usr/local/share/ca-certificates/9router.crt && sudo update-ca-certificates`);
    await startServer(apiKey, password);
  } else throw e;
}

Prevention

When it happens

Trigger: installCert's underlying command fails: sudo password wrong/expired, certutil/security/update-ca-trust binary missing or non-PATH, keychain denies the add, Linux without the expected CA directory, or the CA file is corrupt/just regenerated mid-flight.

Common situations: Wrong sudo password saved in the encrypted store (message like 'incorrect password attempts'); minimal Docker/CI image lacking certutil or update-ca-certificates; corporate-managed macOS where the keychain blocks adding root CAs programmatically; rootCA.crt truncated or expired after an interrupted generateCert; SELinux/AppArmor blocking writes to the system CA dir.

Understand the failure class

Related errors


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