FiloSottile/mkcert · error

failed adding cert: %v

Error message

failed adding cert: %v

What it means

addCert calls CertAddEncodedCertificateToStore with CERT_STORE_ADD_REPLACE_EXISTING on the ROOT store; a zero return means the API rejected the DER bytes. The input is the DER payload decoded from rootCA.pem, so common causes are a malformed/expired/unparseable certificate, or an access-denied on the protected ROOT store despite it being open.

Source

Thrown at truststore_windows.go:104

		return nil
	}
	return fmt.Errorf("failed to close windows root store: %v", err)
}

func (w windowsRootStore) addCert(cert []byte) error {
	// TODO: ok to always overwrite?
	ret, _, err := procCertAddEncodedCertificateToStore.Call(
		uintptr(w), // HCERTSTORE hCertStore
		uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType
		uintptr(unsafe.Pointer(&cert[0])),                              // const BYTE *pbCertEncoded
		uintptr(len(cert)),                                             // DWORD cbCertEncoded
		3,                                                              // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3)
		0,                                                              // PCCERT_CONTEXT *ppCertContext
	)
	if ret != 0 {
		return nil
	}
	return fmt.Errorf("failed adding cert: %v", err)
}

func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
	// Go over each, deleting the ones we find
	var cert *syscall.CertContext
	deletedAny := false
	for {
		// Next enum
		certPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert)))
		if cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil {
			if errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 {
				break
			}
			return deletedAny, fmt.Errorf("failed enumerating certs: %v", err)
		}
		// Parse cert
		certBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length]
		parsedCert, err := x509.ParseCertificate(certBytes)

View on GitHub (pinned to 1c1dc4ed27)

Solutions

  1. Validate the DER independently: `openssl x509 -in rootCA.pem -noout -text` to confirm it parses and inspect algorithm/expiry.
  2. If the CA is corrupt, back up and delete CAROOT, then rerun `mkcert -install` to generate a fresh one.
  3. Run from an elevated terminal to rule out access-denied on the ROOT store.
  4. If a group policy restricts root additions, use the approved distribution mechanism (GPO/MDM) to deploy mkcert's CA instead.

Example fix

# before
mkcert -install   # failed adding cert: ... 

# after: verify then regenerate
openssl x509 -in "$(mkcert -CAROOT)/rootCA.pem" -noout -text
rm "$(mkcert -CAROOT)/rootCA.pem" "$(mkcert -CAROOT)/rootCA-key.pem"
mkcert -install
Defensive patterns

Strategy: validation

Validate before calling

block, _ := pem.Decode(pemBytes)
if block == nil {
    return errors.New("not PEM")
}
if _, err := x509.ParseCertificate(block.Bytes); err != nil {
    return fmt.Errorf("DER does not parse as X.509: %w", err)
}
// only call install after both checks pass

Prevention

When it happens

Trigger: `mkcert -install` where rootCA.pem decodes as PEM but its DER payload is corrupt or truncated (pem.Decode succeeded, the bytes after are not a valid X.509); a certificate Windows refuses (e.g. weak algorithm like a signature Windows' policy disables); insufficient privileges or policy blocking writes to the trusted root store.

Common situations: Editing/syncing tools that strip characters but leave a syntactically valid PEM envelope; CAs generated with algorithms disallowed by Windows crypto policy; enterprise policies that forbid adding self-signed roots; CA file regenerated mid-install by a concurrent mkcert run.

Related errors


AI-assisted analysis of FiloSottile/mkcert@1c1dc4ed27 (2026-08-15). Data as JSON: /api/errors/089d6b6c9740ac83. Report an issue: GitHub.