gohugoio/hugo · error
failed to parse root certificate
Error message
failed to parse root certificate
What it means
`serverCommand.verifyCert` builds an `x509.CertPool` from the mkcert root CA PEM (`rootCA.pem`). `AppendCertsFromPEM` returns false when no certificate could be parsed from the input, and Hugo wraps that as a plain parse error. This blocks reuse of an existing site certificate.
Solutions
- Verify `$CAROOT` is set and `rootCA.pem` exists with valid PEM content.
- Regenerate the root CA with `mkcert -install` (or let Hugo recreate certs).
- Delete the cached site cert under the cache `_mkcerts` dir so Hugo provisions fresh.
- Confirm read permissions on `$CAROOT/rootCA.pem`.
Example fix
// before // $CAROOT/rootCA.pem is 0 bytes // after // $ mkcert -uninstall && mkcert -install
Defensive patterns
Strategy: validation
Validate before calling
import "encoding/pem"
func validateRootCA(path string) error {
b, err := os.ReadFile(path)
if err != nil { return err }
var block *pem.Block
rest := b
found := false
for {
block, rest = pem.Decode(rest)
if block == nil { break }
if block.Type == "CERTIFICATE" { found = true; break }
}
if !found { return fmt.Errorf("rootCA.pem has no CERTIFICATE block") }
return nil
} Type guard
null
Try / catch
// Treat cached-cert reuse failure as a signal to regenerate.
if err := c.verifyCert(rootPem, certPEM, hostname); err != nil {
// fall through to provisioning fresh certs via mkcert
} Prevention
- Keep `$CAROOT` stable and backed up; don't manually edit `rootCA.pem`.
- Re-run `mkcert -install` after OS reinstalls or profile resets.
- Delete the `_mkcerts` cache if certs start failing verification.
When it happens
Trigger: Fires when `rootPem` (read from `$CAROOT/rootCA.pem`) is empty, truncated, or not valid PEM — so the pool rejects it. This is part of the cached-cert reuse path in `createCertificates`.
Common situations: The `$CAROOT` env var points at a stale or emptied directory; `rootCA.pem` was corrupted by an editor; mkcert was partially uninstalled leaving a zero-byte file; permissions prevent reading the full file.
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 PEM
- failed to parse 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/306a019b78fe59f6.
Report an issue: GitHub.
Appendix: source
Thrown at commands/server.go:729
if err := c.verifyCert(rootPem, certPEM, hostname); err == nil {
c.r.Println("Using existing", c.tlsCertFile, "and", c.tlsKeyFile)
return nil
}
}
}
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())View on GitHub (pinned to 52c9bd7908)