hashicorp/nomad · error

failed to decode %s PEM block

Error message

failed to decode %s PEM block

What it means

getCassCert read the cert bytes but pem.Decode found no valid PEM block — the data lacks recognizable BEGIN/END certificate markers or the base64 body is invalid. newlineHeaders fixes surrounding newlines, so the content itself is not valid PEM.

Source

Thrown at lib/auth/oidc/client_assertion.go:176

	if k.PemCertFile != "" {
		source = "PemCertFile"
		bts, err = os.ReadFile(k.PemCertFile)
		if err != nil {
			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 {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify the content starts with `-----BEGIN CERTIFICATE-----` and ends with `-----END CERTIFICATE-----`; re-export with `openssl x509 -in cert.crt -out cert.pem`.
  2. If the value is base64, decode it first: `base64 -d cert.b64 > cert.pem`.
  3. Check the file is non-empty and contains the certificate, not the private key or an error page.

Example fix

// before: base64 blob pasted as PemCert
PemCert: "LS0tLS1CRUdJTi..."
// after: decoded PEM
PemCert: "-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----"
Defensive patterns

Strategy: validation

Validate before calling

func validateCertPEM(s string) error {
  blk, _ := pem.Decode([]byte(s))
  if blk == nil || blk.Type != "CERTIFICATE" {
    return fmt.Errorf("value is not a PEM certificate")
  }
  _, err := x509.ParseCertificate(blk.Bytes)
  return err
}

Type guard

func isPEMCertificate(s string) bool {
  blk, _ := pem.Decode([]byte(s))
  return blk != nil && blk.Type == "CERTIFICATE"
}

Try / catch

key, err := getCassCert(k)
if err != nil && strings.Contains(err.Error(), "failed to decode") {
  return fmt.Errorf("cert value is not PEM; decode base64 or re-export via openssl x509: %w", err)
}

Prevention

When it happens

Trigger: BuildClientAssertionJWT → getCassCert after reading PemCertFile/PemCert, when the bytes are not a PEM certificate (raw DER, JSON-wrapped secret, empty file, HTML error page).

Common situations: Secret store returned the cert base64-encoded and it was pasted without decoding; file contains the key instead of the cert; templating stripped the PEM headers; empty placeholder file.

Understand the failure class

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/2ed6665eee855de7. Report an issue: GitHub.