caddyserver/caddy · error
loading certificates with 'automate' requires array of strin
Error message
loading certificates with 'automate' requires array of strings, got: %T
What it means
The special 'automate' entry under tls.certificates must unmarshal to *AutomateLoader, which is defined as a []string of subject names to be managed by automation. The type assertion modIface.(*AutomateLoader) failed (or yielded nil), meaning the JSON value for "automate" was not a plain array of strings.
Source
Thrown at modules/caddytls/tls.go:240
// certificate loaders
val, err := ctx.LoadModule(t, "CertificatesRaw")
if err != nil {
return fmt.Errorf("loading certificate loader modules: %s", err)
}
for modName, modIface := range val.(map[string]any) {
if modName == "automate" {
// special case; these will be loaded in later using our automation facilities,
// which we want to avoid doing during provisioning
if automateNames, ok := modIface.(*AutomateLoader); ok && automateNames != nil {
if t.automateNames == nil {
t.automateNames = make(map[string]struct{})
}
repl := caddy.NewReplacer()
for _, sub := range *automateNames {
t.automateNames[repl.ReplaceAll(sub, "")] = struct{}{}
}
} else {
return fmt.Errorf("loading certificates with 'automate' requires array of strings, got: %T", modIface)
}
continue
}
t.certificateLoaders = append(t.certificateLoaders, modIface.(CertificateLoader))
}
// using the certificate loaders we just initialized, load
// manual/static (unmanaged) certificates - we do this in
// provision so that other apps (such as http) can know which
// certificates have been manually loaded, and also so that
// commands like validate can be a better test
certCacheMu.RLock()
magic := certmagic.New(certCache, certmagic.Config{
Storage: ctx.Storage(),
Logger: t.logger,
OnEvent: t.onEvent,
OCSP: certmagic.OCSPConfig{
DisableStapling: t.DisableOCSPStapling,View on GitHub (pinned to 50e54ee279)
Solutions
- Change the automate value to a JSON array of hostnames only
- If you need per-name issuer/policy options, move those names into tls.automation.policies subjects instead of the automate loader
- Run 'caddy validate' on the config to catch the type error before runtime
Example fix
// before
{"certificates": {"automate": {"subject": "example.com"}}}
// after
{"certificates": {"automate": ["example.com"]} Defensive patterns
Strategy: type-guard
Validate before calling
import "encoding/json"
var raw json.RawMessage
json.Unmarshal(cfg, &raw)
var certs map[string]json.RawMessage
json.Unmarshal(raw, &certs)
if v, ok := certs["automate"]; ok {
var names []string
if err := json.Unmarshal(v, &names); err != nil {
return fmt.Errorf("automate must be an array of strings")
}
} Type guard
func isAutomateArray(v json.RawMessage) bool {
var names []string
return json.Unmarshal(v, &names) == nil
} Prevention
- Use 'caddy adapt' to generate correct automate JSON from Caddyfile 'tls internal' style directives
- Remember automate accepts only a flat string array
When it happens
Trigger: Writing {"automate": "example.com"} (a bare string), {"automate": [{"subject": "example.com"}]} (array of objects), or any object form under the automate key. Only {"automate": ["example.com", ...]} is valid.
Common situations: Hand-edited JSON where users assume automate takes an object with options; converting from Caddyfile syntax and guessing the JSON shape; copy-paste from configs written for a different fork/schema.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- recombining SNI matchers: %v
- not a valid big integer: %s
- missing 'req' argument
- unsupported type
- private key does not match issuer public key
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/c4121267ec7c1c2d.
Report an issue: GitHub.