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

  1. Verify `$CAROOT` is set and `rootCA.pem` exists with valid PEM content.
  2. Regenerate the root CA with `mkcert -install` (or let Hugo recreate certs).
  3. Delete the cached site cert under the cache `_mkcerts` dir so Hugo provisions fresh.
  4. 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

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

Related errors


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)