{"record":{"id":"6a946a6df1110828","repo":"hashicorp/nomad","slug":"failed-to-parse-root-certificate","errorCode":null,"errorMessage":"failed to parse root certificate","messagePattern":"failed to parse root certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/tlsutil/generate.go","lineNumber":349,"sourceCode":"\t\t\treturn nil, err\n\t\t}\n\t\tpk, ok := signer.(crypto.Signer)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"private key is not a valid format\")\n\t\t}\n\n\t\treturn pk, nil\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown PEM block type for signing key: %s\", block.Type)\n\t}\n}\n\nfunc Verify(caString, certString, dns string) error {\n\troots := x509.NewCertPool()\n\tok := roots.AppendCertsFromPEM([]byte(caString))\n\tif !ok {\n\t\treturn fmt.Errorf(\"failed to parse root certificate\")\n\t}\n\n\tcert, err := parseCert(certString)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse certificate\")\n\t}\n\n\topts := x509.VerifyOptions{\n\t\tDNSName: fmt.Sprint(dns),\n\t\tRoots:   roots,\n\t}\n\n\t_, err = cert.Verify(opts)\n\treturn err\n}\n","sourceCodeStart":331,"sourceCodeEnd":365,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/tlsutil/generate.go#L331-L365","documentation":"Verify builds an x509.CertPool from caString; AppendCertsFromPEM returns false when none of the bytes decode as a certificate, so Verify reports the CA PEM could not be parsed and returns immediately without attempting chain verification.","triggerScenarios":"Calling tlsutil.Verify(ca, cert, dns) where the ca argument is empty, truncated, contains only a private key, or is otherwise not PEM certificate data.","commonSituations":"Config value pointing at the wrong file (leaf cert or key instead of CA); missing newline/whitespace corruption after template rendering; CA supplied in DER format instead of PEM.","solutions":["Print/inspect the ca string or file and confirm it starts with '-----BEGIN CERTIFICATE-----'","Regenerate or re-export the CA certificate in PEM encoding","Verify the config path points to the CA bundle file, not the cert or key"],"exampleFix":"// before\ncertPool.AppendCertsFromPEM([]byte(keyPEM)) // wrong file: private key\n// after\ncertPool.AppendCertsFromPEM([]byte(caPEM)) // must be CA certificate PEM","handlingStrategy":"validation","validationCode":"func looksLikeCertPEM(s string) bool {\n\tblock, _ := pem.Decode([]byte(s))\n\tif block == nil || block.Type != \"CERTIFICATE\" {\n\t\treturn false\n\t}\n\t_, err := x509.ParseCertificate(block.Bytes)\n\treturn err == nil\n}\nif !looksLikeCertPEM(caString) {\n\treturn errors.New(\"CA bundle is not valid PEM certificate data\")\n}\nerr := tlsutil.Verify(caString, certString, dns)","typeGuard":"func isValidCA(ca string) bool {\n\tpool := x509.NewCertPool()\n\treturn pool.AppendCertsFromPEM([]byte(ca))\n}","tryCatchPattern":"if err := tlsutil.Verify(caPEM, certPEM, dns); err != nil {\n\tif err.Error() == \"failed to parse root certificate\" {\n\t\treturn fmt.Errorf(\"CA bundle unreadable — verify ca config points at a PEM CA cert: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Pre-load the CA into an x509.CertPool at startup to fail fast","Keep CA, leaf cert, and key in distinct config fields/files","Use PEM encoding only; convert DER certs before use","Validate rendered config/templates for truncation"],"tags":["tls","x509","certificate","pem"],"backgroundTag":"certificate-pem-parse-error","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}