caddyserver/caddy · error

caching unmanaged certificate: %v

Error message

caching unmanaged certificate: %v

What it means

Each certificate returned by a loader is put into the shared certmagic cache via magic.CacheUnmanagedTLSCertificate. This error means certmagic rejected the parsed certificate: commonly the leaf certificate is not valid for any names, is expired in a way certmagic rejects, has no names extractable from SANs/CN, or the TLS handshake configuration it builds (key type) is unsupported.

Source

Thrown at modules/caddytls/tls.go:271

	magic := certmagic.New(certCache, certmagic.Config{
		Storage: ctx.Storage(),
		Logger:  t.logger,
		OnEvent: t.onEvent,
		OCSP: certmagic.OCSPConfig{
			DisableStapling: t.DisableOCSPStapling,
		},
		DisableStorageCheck: t.DisableStorageCheck,
	})
	certCacheMu.RUnlock()
	for _, loader := range t.certificateLoaders {
		certs, err := loader.LoadCertificates()
		if err != nil {
			return fmt.Errorf("loading certificates: %v", err)
		}
		for _, cert := range certs {
			hash, err := magic.CacheUnmanagedTLSCertificate(ctx, cert.Certificate, cert.Tags)
			if err != nil {
				return fmt.Errorf("caching unmanaged certificate: %v", err)
			}
			t.loaded[hash] = ""
		}
	}

	// on-demand permission module
	if t.Automation != nil && t.Automation.OnDemand != nil && t.Automation.OnDemand.PermissionRaw != nil {
		if t.Automation.OnDemand.Ask != "" {
			return fmt.Errorf("on-demand TLS config conflict: both 'ask' endpoint and a 'permission' module are specified; 'ask' is deprecated, so use only the permission module")
		}
		val, err := ctx.LoadModule(t.Automation.OnDemand, "PermissionRaw")
		if err != nil {
			return fmt.Errorf("loading on-demand TLS permission module: %v", err)
		}
		t.Automation.OnDemand.permission = val.(OnDemandPermission)
	}

	// automation/management policies

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Inspect the wrapped error from certmagic; it distinguishes parse vs. handshake-configuration failures
  2. Inspect the cert: 'openssl x509 -in cert.pem -noout -text' — confirm SAN entries exist and cover intended names
  3. Re-encode the key/cert to standard RSA-2048/ECDSA PEM if produced by exotic tooling
  4. Confirm the key file passed alongside actually corresponds to the certificate

Example fix

# before: cert with no SAN, only CN (rejected)
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout k.pem -out c.pem -subj "/CN=example.com"
# after: include SAN
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout k.pem -out c.pem -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com"
Defensive patterns

Strategy: validation

Validate before calling

openssl x509 -in cert.pem -noout -ext subjectAltName  # must list DNS names
openssl verify -CAfile cert.pem cert.pem

Prevention

When it happens

Trigger: Loading a cert whose SAN list is empty; a certificate parsed but whose tags/names cannot be determined; an unsupported private key algorithm (e.g. Ed448 or an encrypted PKCS#8 key that cannot be loaded); corrupted DER inside an otherwise valid PEM envelope.

Common situations: Self-generated certs created with unusual OpenSSL options; certs from HSMs or cloud services exported in non-standard ways; mixing up certificate and key file order in load_files entries.

Understand the failure class

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/7caa836a2f98323b. Report an issue: GitHub.