{"record":{"id":"ac6e49acc814d9bb","repo":"nats-io/nats-server","slug":"failed-to-parse-root-ca-certificate","errorCode":null,"errorMessage":"failed to parse root ca certificate","messagePattern":"failed to parse root ca certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/opts.go","lineNumber":5883,"sourceCode":"\t\t\t}\n\t\t\tconfig.Certificates[i] = cert\n\t\t}\n\t}\n\n\t// Require client certificates as needed\n\tif tc.Verify {\n\t\tconfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t}\n\t// Add in CAs if applicable.\n\tif tc.CaFile != _EMPTY_ {\n\t\trootPEM, err := os.ReadFile(tc.CaFile)\n\t\tif err != nil || rootPEM == nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpool := x509.NewCertPool()\n\t\tok := pool.AppendCertsFromPEM(rootPEM)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse root ca certificate\")\n\t\t}\n\t\tconfig.ClientCAs = pool\n\t}\n\t// Allow setting TLS minimum version.\n\tif tc.MinVersion > 0 {\n\t\tif tc.MinVersion < tls.VersionTLS12 {\n\t\t\treturn nil, fmt.Errorf(\"unsupported minimum TLS version: %s\", tls.VersionName(tc.MinVersion))\n\t\t}\n\t\tconfig.MinVersion = tc.MinVersion\n\t}\n\n\treturn &config, nil\n}\n\n// MergeOptions will merge two options giving preference to the flagOpts\n// if the item is present.\nfunc MergeOptions(fileOpts, flagOpts *Options) *Options {\n\tif fileOpts == nil {","sourceCodeStart":5865,"sourceCodeEnd":5901,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/opts.go#L5865-L5901","documentation":"The NATS server could not add any certificate from the configured root CA PEM file into an x509.CertPool. x509 pool.AppendCertsFromPEM silently returns false when the file contains no parseable CERTIFICATE PEM blocks, and the server converts that into this error when building the TLS config's ClientCAs for mTLS.","triggerScenarios":"Setting the trust/CA option (ca_file in the TLS config block) to a file with no valid PEM certificates, e.g. an empty file, a DER-encoded cert, or a private key file.","commonSituations":"Pointing ca_file at the server's own cert/key instead of the CA bundle, DER-format CAs from Windows exports, empty or truncated file, wrong path resolution relative to the config file, or a bundle containing only expired/foreign blocks that fail to parse.","solutions":["Validate the CA file with `openssl x509 -in ca.pem -text -noout` and confirm it prints a certificate.","Point ca_file at the actual CA certificate/bundle, not the server cert or key.","Convert DER to PEM: `openssl x509 -inform der -in ca.cer -out ca.pem`.","Check the file is non-empty and readable at the resolved path (relative to cwd or config dir)."],"exampleFix":"// before\nca_file: \"./server.key\"\n// after\nca_file: \"./certs/ca.pem\"","handlingStrategy":"validation","validationCode":"// Verify CA file contains at least one parseable certificate before pointing ca_file at it\ncaPEM, err := os.ReadFile(caFile)\nif err != nil { return err }\npool := x509.NewCertPool()\nif !pool.AppendCertsFromPEM(caPEM) {\n    return fmt.Errorf(\"%s contains no valid PEM certificates\", caFile)\n}","typeGuard":"func isValidCAPEM(path string) bool {\n    pemBytes, err := os.ReadFile(path)\n    if err != nil { return false }\n    return x509.NewCertPool().AppendCertsFromPEM(pemBytes)\n}","tryCatchPattern":"if !pool.AppendCertsFromPEM(rootPEM) {\n    return fmt.Errorf(\"CA file %s has no parseable PEM certs; run: openssl x509 -in %s -text -noout\", caPath, caPath)\n}","preventionTips":["Always point ca_file at a CA bundle, never at server cert/key files.","Convert Windows/DER CAs to PEM with `openssl x509 -inform der` before use.","Check the file resolves relative to the config file's directory."],"tags":["tls","certificate","ca","mtls"],"backgroundTag":"ca-certificate-pem-parse-failed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}