caddyserver/caddy · critical
decoding intermediate key: %v
Error message
decoding intermediate key: %v
What it means
The intermediate key PEM bytes were loaded from storage, but certmagic.PEMDecodePrivateKey could not decode them into a signer. The stored key is invalid: wrong PEM block type, encrypted key, unsupported format, or corrupted base64. The intermediate chain may be perfectly fine, but signing is impossible without a usable key.
Source
Thrown at modules/caddypki/ca.go:376
interCertChain = append(interCertChain, interCert)
}
if len(interCertChain) == 0 {
interCertChain, err = pemDecodeCertificateChain(interCertPEM)
if err != nil {
return nil, nil, fmt.Errorf("decoding intermediate certificate PEM: %v", err)
}
}
if interKey == nil {
interKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateKey())
if err != nil {
return nil, nil, fmt.Errorf("loading intermediate key: %v", err)
}
interKey, err = certmagic.PEMDecodePrivateKey(interKeyPEM)
if err != nil {
return nil, nil, fmt.Errorf("decoding intermediate key: %v", err)
}
}
return interCertChain, interKey, nil
}
func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCert *x509.Certificate, interKey crypto.Signer, err error) {
repl := ca.newReplacer()
interCert, interKey, err = generateIntermediate(repl.ReplaceAll(ca.IntermediateCommonName, ""), rootCert, rootKey, time.Duration(ca.IntermediateLifetime))
if err != nil {
return nil, nil, fmt.Errorf("generating CA intermediate: %v", err)
}
interCertPEM, err := pemEncodeCert(interCert.Raw)
if err != nil {
return nil, nil, fmt.Errorf("encoding intermediate certificate: %v", err)
}
err = ca.storage.Store(ca.ctx, ca.storageKeyIntermediateCert(), interCertPEM)View on GitHub (pinned to 50e54ee279)
Solutions
- Restore a valid, unencrypted PEM private key matching intermediate.crt (validate: openssl pkey -in key.pem -noout; decrypt with openssl pkey first if needed).
- If no valid key remains, delete the intermediate cert+key objects and restart so Caddy regenerates the intermediate under the existing root.
- Confirm key/cert pairing by comparing public keys.
- Keep storage objects untouched by external tooling.
Example fix
# before: encrypted key in storage -----BEGIN ENCRYPTED PRIVATE KEY-----... # after openssl pkey -in enc.key -out intermediate.key.pem -----BEGIN PRIVATE KEY-----...
Defensive patterns
Strategy: validation
Validate before calling
// Reject undecodable keys before startup
block, _ := pem.Decode(interKeyPEM)
if block == nil || strings.Contains(block.Type, "ENCRYPTED") { fail("intermediate key must be unencrypted PEM") } Type guard
func isUsablePrivateKeyPEM(b []byte) bool {
blk, _ := pem.Decode(b)
if blk == nil || strings.Contains(blk.Type, "ENCRYPTED") { return false }
_, err := certmagicPEMDecodePrivateKeyMock(b) // or x509.ParsePKCS8PrivateKey etc.
return err == nil
} Try / catch
if strings.Contains(err.Error(), "decoding intermediate key") {
// re-store an unencrypted PEM key matching intermediate.crt, or regenerate intermediate
} Prevention
- Decrypt OpenSSL keys before placing them in Caddy storage.
- Validate key files with openssl pkey -noout in deploy pipelines.
- Restrict shell access to storage directories to reduce accidental rewrites.
When it happens
Trigger: intermediate key object contains an encrypted ('BEGIN ENCRYPTED PRIVATE KEY') or otherwise undecodable PEM, a certificate instead of a key, raw DER, or truncated bytes. Raised in loadOrGenIntermediate after the cert chain loaded successfully.
Common situations: Operator replaced the key with an OpenSSL-exported passphrase-protected key; scripts writing DER; truncation after disk-full; copy/paste errors when restoring from backups.
Related errors
- decoding root key: %v
- generating root: %v
- parsing root certificate PEM: %v
- generating CA root: %v
- encoding root certificate: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/b61e8abc5b4b84a6.
Report an issue: GitHub.