{"record":{"id":"07a03d1d104eac7d","repo":"caddyserver/caddy","slug":"invalid-key-type-s-s","errorCode":null,"errorMessage":"invalid key type %s: %s","messagePattern":"invalid key type (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/automation.go","lineNumber":279,"sourceCode":"\t\tif annoying, ok := issuer.(ConfigSetter); ok {\n\t\t\tannoying.SetConfig(ap.magic)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// makeCertMagicConfig constructs a certmagic.Config for this policy using the\n// provided issuers and storage. It encapsulates common logic shared between\n// Provision and RebuildCertMagic so we don't duplicate code.\nfunc (ap *AutomationPolicy) makeCertMagicConfig(tlsApp *TLS, issuers []certmagic.Issuer, storage certmagic.Storage) (certmagic.Config, error) {\n\t// key source\n\tkeyType := ap.KeyType\n\tif keyType != \"\" {\n\t\tvar err error\n\t\tkeyType, err = caddy.NewReplacer().ReplaceOrErr(ap.KeyType, true, true)\n\t\tif err != nil {\n\t\t\treturn certmagic.Config{}, fmt.Errorf(\"invalid key type %s: %s\", ap.KeyType, err)\n\t\t}\n\t\tif _, ok := supportedCertKeyTypes[keyType]; !ok {\n\t\t\treturn certmagic.Config{}, fmt.Errorf(\"unrecognized key type: %s\", keyType)\n\t\t}\n\t}\n\tkeySource := certmagic.StandardKeyGenerator{\n\t\tKeyType: supportedCertKeyTypes[keyType],\n\t}\n\n\tif storage == nil {\n\t\tstorage = tlsApp.ctx.Storage()\n\t}\n\n\t// on-demand TLS\n\tvar ond *certmagic.OnDemandConfig\n\tif ap.OnDemand || len(ap.Managers) > 0 {\n\t\t// permission module is now required after a number of negligence cases that allowed abuse;\n\t\t// but it may still be optional for explicit subjects (bounded, non-wildcard), for the","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/automation.go#L261-L297","documentation":"The automation policy's key_type field is run through Caddy's placeholder replacer (ReplaceOrErr with error-on-empty and error-on-unknown) and the replacement itself failed. This means key_type contained a placeholder like {$KEY_TYPE} or {env.KEY_TYPE} that is either unset (expanded to empty) or not a recognized placeholder.","triggerScenarios":"Setting key_type to a placeholder whose environment variable is undefined at config-load time, or using {foo.bar} syntax for a placeholder that does not exist while hard-on-unknown replacement is enabled.","commonSituations":"Templated configs meant to be parameterized by env vars where the variable was forgotten in systemd unit/container env; moving a config between machines with different env setups.","solutions":["Set the referenced environment variable (e.g. KEY_TYPE=p256) in the service environment and reload Caddy.","Or replace the placeholder with a literal key type: rsa2048, rsa4096, p256, p384, or ed25519.","If the placeholder should be optional, remove it from key_type and rely on the default (p256/ECDSA via certmagic defaults)."],"exampleFix":"# before\nexample.com {\n\ttls {\n\t\tkey_type {$KEY_TYPE}\n\t}\n}\n# KEY_TYPE unset\n\n# after\nexport KEY_TYPE=p256   # in the caddy service environment\n# or hardcode:\ntls {\n\tkey_type p256\n}","handlingStrategy":"validation","validationCode":"// Reject key types that reference unset placeholders before provisioning.\nif strings.Contains(policy.KeyType, \"{\") {\n    if v, err := caddy.NewReplacer().ReplaceOrErr(policy.KeyType, true, true); err != nil || v == \"\" {\n        return fmt.Errorf(\"key_type placeholder %q unresolved\", policy.KeyType)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document required env vars per deployment and assert them in startup scripts.","Avoid placeholders in key_type unless the value is guaranteed present."],"tags":["tls","configuration","placeholders","environment"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}