{"record":{"id":"166e736e56d0c138","repo":"hashicorp/nomad","slug":"failed-to-parse-cert-key-pair-w","errorCode":null,"errorMessage":"failed to parse cert key pair: %w","messagePattern":"failed to parse cert key pair: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"command/agent/tls_metrics.go","lineNumber":66,"sourceCode":"func newTLSMetrics(logger hclog.Logger, tlsCfg *config.TLSConfig, labels []metrics.Label) (*tlsMetrics, error) {\n\n\tt := tlsMetrics{\n\t\tlabels: labels,\n\t\tlogger: logger,\n\t\tstopCh: make(chan struct{}),\n\t}\n\n\texp, err := caFileExpiry(tlsCfg.CAFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse CA file: %w\", err)\n\t}\n\tt.caExpiry = exp\n\n\t// Using LoadX509KeyPair helps with parsing files with combined\n\t// public/private keys, whitespace, etc.\n\tcerts, err := tls.LoadX509KeyPair(tlsCfg.CertFile, tlsCfg.KeyFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse cert key pair: %w\", err)\n\t}\n\n\t// we are guaranteed to have at least 1 cert if LoadX509 succeeds\n\tc, err := x509.ParseCertificate(certs.Certificate[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse cert bytes: %w\", err)\n\t}\n\tt.certExpiry = c.NotAfter\n\n\treturn &t, nil\n}\n\n// start launches the background goroutine that emits TLS certificate expiry\n// metrics at regular intervals. The interval is defined by the caller and\n// should be based on the agents telemetry configuration.\nfunc (t *tlsMetrics) start(interval time.Duration) {\n\tt.logger.Info(\"starting TLS expiration metric process\")\n\tgo t.emitLoop(interval)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/tls_metrics.go#L48-L84","documentation":"newTLSMetrics loads the agent's TLS certificate and key via crypto/tls.LoadX509KeyPair. If the cert/key files are missing, malformed, mismatched, or encrypted, the error is wrapped as 'failed to parse cert key pair:' and agent startup fails.","triggerScenarios":"tls { cert_file, key_file } configured, and tls.LoadX509KeyPair fails at NewAgent time — bad paths, invalid PEM, key not matching certificate, or encrypted private key.","commonSituations":"Renewed certificate deployed without the matching key (or vice versa), wrong file paths, key in PKCS#8/encrypted format not accepted, whitespace/concatenation issues in combined PEM files.","solutions":["Test the pair with 'openssl x509 -noout -modulus -in cert.crt' and 'openssl rsa -noout -modulus -in key.pem' and compare moduli; reissue if mismatched","Verify cert_file and key_file paths exist and are readable by the agent user","Ensure the key is an unencrypted PEM; decrypt or convert format if needed","Redeploy cert and key together from the same issuance"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if _, err := tls.LoadX509KeyPair(certFile, keyFile); err != nil {\n    return fmt.Errorf(\"cert/key pair invalid: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"certs, err := tls.LoadX509KeyPair(tlsCfg.CertFile, tlsCfg.KeyFile)\nif err != nil {\n    return fmt.Errorf(\"failed to parse cert key pair: %w\", err)\n}","preventionTips":["Deploy cert and key from the same issuance, atomically together","Verify pair match by comparing 'openssl x509 -modulus' and 'openssl rsa -modulus' outputs","Keep keys unencrypted and in PEM format","Check file paths and permissions before agent startup"],"tags":["tls","x509","certificate","nomad"],"backgroundTag":"tls-certificate-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"}