gohugoio/hugo · error
failed to parse certificate PEM
Error message
failed to parse certificate PEM
What it means
`verifyCert` decodes the cached site certificate PEM with `pem.Decode`; a `nil` block means the input is not a PEM-encoded certificate. Hugo returns this plain error before attempting `x509.ParseCertificate`. It indicates the cached `.pem` cert file is corrupt.
Solutions
- Inspect the cached cert PEM file and confirm it has a `-----BEGIN CERTIFICATE-----` block.
- Delete the corrupted `_mkcerts/<hostname>.pem` and key so Hugo regenerates them.
- Re-run `mkcert <hostname>` if managing certs manually.
- Ensure no process truncates the cache directory mid-run.
Example fix
// before // _mkcerts/localhost.pem contains only PRIVATE KEY block // after // rm _mkcerts/localhost*.pem && hugo server --tlsAuto (regenerates)
Defensive patterns
Strategy: validation
Validate before calling
import "encoding/pem"
func validateCertPEM(path string) error {
b, err := os.ReadFile(path)
if err != nil { return err }
if pem.Decode(b) == nil { return fmt.Errorf("not a valid PEM certificate") }
return nil
} Type guard
null
Try / catch
// If the cached cert PEM is unparseable, delete and regenerate.
if _, err := pem.Decode(certPEM); err == nil { /* invalid */ }
// fallback: re-run mkcert or Hugo --tlsAuto Prevention
- Don't overwrite cert PEM files with key-only content.
- Validate cached certs with `openssl x509 -in cert.pem -noout -text`.
- Treat the `_mkcerts` cache as disposable; regenerate when in doubt.
When it happens
Trigger: Fires when the cached `cert.pem` (at `keyDir/<hostname>.pem`) is empty, not PEM-formatted, or contains only a private key block instead of a certificate.
Common situations: The site cert PEM was overwritten with key-only content; truncated by a failed write/interrupt; an editor saved it as a different encoding; the file was swapped with a non-PEM artifact.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse certificate
- failed to parse root certificate
- failed to verify certificate
- --appendPort=false not supported when in multihost mode
- cannot use --renderToMemory with --destination
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/db63f25d3a36cc54.
Report an issue: GitHub.
Appendix: source
Thrown at commands/server.go:734
}
c.r.Println("Creating TLS certificates in", keyDir)
// Yes, this is unfortunate, but it's currently the only way to use Mkcert as a library.
os.Args = []string{"-cert-file", c.tlsCertFile, "-key-file", c.tlsKeyFile, hostname}
return mclib.RunMain()
}
func (c *serverCommand) verifyCert(rootPEM, certPEM []byte, name string) error {
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM(rootPEM)
if !ok {
return fmt.Errorf("failed to parse root certificate")
}
block, _ := pem.Decode(certPEM)
if block == nil {
return fmt.Errorf("failed to parse certificate PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse certificate: %v", err.Error())
}
opts := x509.VerifyOptions{
DNSName: name,
Roots: roots,
}
if _, err := cert.Verify(opts); err != nil {
return fmt.Errorf("failed to verify certificate: %v", err.Error())
}
return nil
}
View on GitHub (pinned to 52c9bd7908)