{"record":{"id":"cab98b8cec7fe7ac","repo":"tailscale/tailscale","slug":"unsupport-cert-mode-q","errorCode":null,"errorMessage":"unsupport cert mode: %q","messagePattern":"unsupport cert mode: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/derper/cert.go","lineNumber":106,"sourceCode":"\t\t\t\tKey: keyBytes,\n\t\t\t}\n\t\t}\n\t\tif hostname == \"derp.tailscale.com\" {\n\t\t\tcertManager.HostPolicy = prodAutocertHostPolicy\n\t\t}\n\t\tif email != \"\" {\n\t\t\tcertManager.Email = email\n\t\t} else if hostname == \"derp.tailscale.com\" {\n\t\t\tcertManager.Email = \"security@tailscale.com\"\n\t\t}\n\t\tif ipCerts {\n\t\t\treturn newIPCertManager(dir, email, \"\", certManager)\n\t\t}\n\t\treturn certManager, nil\n\tcase \"manual\":\n\t\treturn NewManualCertManager(dir, hostname)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupport cert mode: %q\", mode)\n\t}\n}\n\ntype manualCertManager struct {\n\tcert       *tls.Certificate\n\thostname   string // hostname or IP address of server\n\tnoHostname bool   // whether hostname is an IP address\n}\n\n// NewManualCertManager returns a cert provider which read certificate by given hostname on create.\nfunc NewManualCertManager(certdir, hostname string) (certProvider, error) {\n\tkeyname := unsafeHostnameCharacters.ReplaceAllString(hostname, \"\")\n\tcrtPath := filepath.Join(certdir, keyname+\".crt\")\n\tkeyPath := filepath.Join(certdir, keyname+\".key\")\n\tcert, err := tls.LoadX509KeyPair(crtPath, keyPath)\n\thostnameIP := net.ParseIP(hostname) // or nil if hostname isn't an IP address\n\tif err != nil {\n\t\t// If the hostname is an IP address, automatically create a","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/derper/cert.go#L88-L124","documentation":"certProviderByCertMode switches on derper's --certmode flag; only \"letsencrypt\", \"gcp\" (Google Public CA ACME with EAB), and \"manual\" (files from --certdir) are implemented. Any other string reaches the default branch and returns this error (note the source's typo 'unsupport').","triggerScenarios":"Launching derper with a misspelled or removed --certmode value such as letsencrypt2, auto, self-signed, or a value from an outdated doc/tutorial.","commonSituations":"Copy-pasted derper commands from old blog posts; scripts pinning flags that a Tailscale version dropped; empty --certmode combined with custom flag handling that passes it through non-empty.","solutions":["Use --certmode=letsencrypt (ACME/Let's Encrypt, the usual default)","Use --certmode=manual with cert/key files named <hostname>.crt/.key in --certdir","Use --certmode=gcp together with --acme-eab-kid, --acme-eab-key and --acme-email","Check derper --help for the exact flag spellings in your binary version"],"exampleFix":"# before: derper --hostname=derp.example.com --certmode=letsencrypt2\n# after:  derper --hostname=derp.example.com --certmode=letsencrypt","handlingStrategy":"validation","validationCode":"var validCertModes = map[string]bool{\"letsencrypt\": true, \"gcp\": true, \"manual\": true}\nif !validCertModes[mode] {\n\treturn fmt.Errorf(\"--certmode must be one of letsencrypt, gcp, manual; got %q\", mode)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive derper launch flags from a reviewed template, not blog posts","Add a config lint step for the derper unit/manifest","Check `derper --help` after version upgrades"],"tags":["derper","certmode","configuration","tls"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}