caddyserver/caddy · error
unrecognized key type: %s
Error message
unrecognized key type: %s
What it means
After placeholder expansion, the automation policy's key_type is not one of the supported certificate key types. Caddy's supportedCertKeyTypes map accepts exactly: rsa2048, rsa4096, p256, p384, ed25519. Anything else — including common guesses like 'RSA2048', 'rsa', 'P256', 'ecdsa', or 'ed' — is rejected.
Source
Thrown at modules/caddytls/automation.go:282
}
return nil
}
// makeCertMagicConfig constructs a certmagic.Config for this policy using the
// provided issuers and storage. It encapsulates common logic shared between
// Provision and RebuildCertMagic so we don't duplicate code.
func (ap *AutomationPolicy) makeCertMagicConfig(tlsApp *TLS, issuers []certmagic.Issuer, storage certmagic.Storage) (certmagic.Config, error) {
// key source
keyType := ap.KeyType
if keyType != "" {
var err error
keyType, err = caddy.NewReplacer().ReplaceOrErr(ap.KeyType, true, true)
if err != nil {
return certmagic.Config{}, fmt.Errorf("invalid key type %s: %s", ap.KeyType, err)
}
if _, ok := supportedCertKeyTypes[keyType]; !ok {
return certmagic.Config{}, fmt.Errorf("unrecognized key type: %s", keyType)
}
}
keySource := certmagic.StandardKeyGenerator{
KeyType: supportedCertKeyTypes[keyType],
}
if storage == nil {
storage = tlsApp.ctx.Storage()
}
// on-demand TLS
var ond *certmagic.OnDemandConfig
if ap.OnDemand || len(ap.Managers) > 0 {
// permission module is now required after a number of negligence cases that allowed abuse;
// but it may still be optional for explicit subjects (bounded, non-wildcard), for the
// internal issuer since it doesn't cause public PKI pressure on ACME servers; subtly, it
// is useful to allow on-demand TLS to be enabled so Managers can be used, but to still
// prevent issuance from Issuers (when Managers don't provide a certificate) if there's noView on GitHub (pinned to 50e54ee279)
Solutions
- Use one of the exact supported values: rsa2048, rsa4096, p256, p384, ed25519 (lowercase).
- For ECDSA P-256 (the default) you can simply omit key_type.
- Remove any surrounding whitespace or quotes artifacts from the value.
Example fix
# before
example.com {
tls {
key_type RSA-2048
}
}
# after
example.com {
tls {
key_type rsa2048
}
} Defensive patterns
Strategy: type-guard
Validate before calling
var allowedKeyTypes = map[string]bool{"rsa2048": true, "rsa4096": true, "p256": true, "p384": true, "ed25519": true}
if kt := policy.KeyType; kt != "" && !allowedKeyTypes[kt] {
return fmt.Errorf("key_type %q invalid; want one of rsa2048, rsa4096, p256, p384, ed25519", kt)
} Type guard
func isValidKeyType(kt string) bool {
switch kt {
case "", "rsa2048", "rsa4096", "p256", "p384", "ed25519":
return true
}
return false
} Prevention
- Treat the key-type list as an enum in config templating (never free-form).
- Remember values are lowercase and case-sensitive.
- Omit key_type to accept the sane default.
When it happens
Trigger: Setting key_type (Caddyfile) or "key_type" (JSON) to a string outside the supported set, including wrong casing (matching is case-sensitive) or algorithm-family names like 'ecdsa'/'rsa' without a size.
Common situations: Copy-pasting key type names from OpenSSL configs ('RSA-2048') or other servers; assuming case-insensitivity; using 'P256' instead of 'p256'; using 'rsa2048' vs intending 'rsa4096'.
Related errors
- could not convert automation policy subject '%s' to punycode
- invalid TLS renegotiation level: %v
- unsupported cipher suite: %s
- protocol min (%x) cannot be greater than protocol max (%x)
- invalid action type
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/a22c630b3b98b621.
Report an issue: GitHub.