hashicorp/nomad · error
failed to parse %s bytes: %w
Error message
failed to parse %s bytes: %w
What it means
getCassCert decodes a PEM blob and parses the first certificate. This error wraps the x509.ParseCertificate failure, meaning the PEM block decoded successfully but its DER payload is not a valid X.509 certificate (or is truncated/corrupt). The wrapped error (%w) carries the underlying asn.1 parse detail.
Source
Thrown at lib/auth/oidc/client_assertion.go:180
return nil, fmt.Errorf("error reading %s: %w", source, err)
}
}
// or pem string
if k.PemCert != "" {
source = "PemCert"
bts = []byte(k.PemCert)
}
// ensure newlines around pem header/footer
bts = newlineHeaders(bts)
block, _ := pem.Decode(bts)
if block == nil {
return nil, fmt.Errorf("failed to decode %s PEM block", source)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse %s bytes: %w", source, err)
}
now := time.Now()
if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
return nil, errors.New("certificate has expired or is not yet valid")
}
return cert, nil
}
// hashKeyID derives a "certificate thumbprint" that the OIDC provider uses
// to find the certificate to verify the private key JWT signature.
// https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.7
func hashKeyID(cert *x509.Certificate, header structs.OIDCClientAssertionKeyIDHeader) (string, error) {
var hasher hash.Hash
switch header {
case structs.OIDCClientAssertionHeaderX5t:
if fips140.Enabled() {
return "", errors.New("x5t assertion headers use SHA-1, which is forbidden in FIPS-140 mode")
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the file and confirm the block is a full '-----BEGIN CERTIFICATE-----' PEM with an intact base64 body
- If the file is a private key or public key, export the matching certificate (e.g. openssl x509 -in req.pem -out cert.pem) and point the config at it
- Verify integrity with: openssl x509 -in <file> -noout -text; fix or regenerate the certificate
- Regenerate the keypair/certificate and re-upload via the OIDC client upsert
Example fix
// before (config pointed at private key) client_assertion_key = "/etc/nomad/oidc/client.key" // after (point at the X.509 certificate) client_assertion_key = "/etc/nomad/oidc/client.crt"
Defensive patterns
Strategy: validation
Validate before calling
pemBytes, _ := os.ReadFile(certPath)
block, _ := pem.Decode(pemBytes)
if block == nil || block.Type != "CERTIFICATE" {
return fmt.Errorf("%s is not a CERTIFICATE PEM block", certPath)
}
if _, err := x509.ParseCertificate(block.Bytes); err != nil {
return fmt.Errorf("cert at %s is not valid DER: %w", certPath, err)
} Type guard
func isCertificatePEM(b []byte) bool {
block, _ := pem.Decode(b)
return block != nil && block.Type == "CERTIFICATE"
} Prevention
- Point config at the .crt/.pem certificate, never the private key file
- Validate with `openssl x509 -in file -noout` during provisioning
- Keep PEM files intact — avoid manual copy/paste edits that truncate base64
- Verify certificate validity window (NotBefore/NotAfter) before deploying
When it happens
Trigger: BuildClientAssertionJWT loads a key/cert blob via getCassCert where pem.Decode succeeds but block.Bytes is not parseable DER — e.g. the file contains a PUBLIC KEY, PRIVATE KEY, CSR, or arbitrary base64 block instead of a CERTIFICATE; or the cert body was truncated/edited.
Common situations: Misconfigured Nomad OIDC client assertion key file pointing at a private key (.key) or public key instead of the certificate; copy-paste truncating PEM body; wrong file mounted in a secret; multi-document PEM where the first block is not the certificate.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse root certificate
- failed to parse certificate
- failed to decode %s PEM block
- no PEM-encoded data found
- certificate has expired or is not yet valid
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/37952569ffda2b40.
Report an issue: GitHub.