{"record":{"id":"05467b6e3eda9003","repo":"caddyserver/caddy","slug":"expanding-email-address-s-v","errorCode":null,"errorMessage":"expanding email address '%s': %v","messagePattern":"expanding email address '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/acmeissuer.go","lineNumber":138,"sourceCode":"// CaddyModule returns the Caddy module information.\nfunc (ACMEIssuer) CaddyModule() caddy.ModuleInfo {\n\treturn caddy.ModuleInfo{\n\t\tID:  \"tls.issuance.acme\",\n\t\tNew: func() caddy.Module { return new(ACMEIssuer) },\n\t}\n}\n\n// Provision sets up iss.\nfunc (iss *ACMEIssuer) Provision(ctx caddy.Context) error {\n\tiss.logger = ctx.Logger()\n\n\trepl := caddy.NewReplacer()\n\n\t// expand email address, if non-empty\n\tif iss.Email != \"\" {\n\t\temail, err := repl.ReplaceOrErr(iss.Email, true, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"expanding email address '%s': %v\", iss.Email, err)\n\t\t}\n\t\tiss.Email = email\n\t}\n\n\t// expand CA endpoint, if non-empty\n\tif iss.CA != \"\" {\n\t\tca, err := repl.ReplaceOrErr(iss.CA, true, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"expanding CA endpoint '%s': %v\", iss.CA, err)\n\t\t}\n\t\tiss.CA = ca\n\t}\n\n\t// expand TestCA endpoint, if non-empty\n\tif iss.TestCA != \"\" {\n\t\ttestca, err := repl.ReplaceOrErr(iss.TestCA, true, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"expanding TestCA endpoint '%s': %v\", iss.TestCA, err)","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/acmeissuer.go#L120-L156","documentation":"Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:138) when the configured email address contains a placeholder and the Caddy replacer fails to expand it. ReplaceOrErr is called with errorOnUnset=true, so an unknown/unresolvable placeholder or a malformed placeholder expression is an error rather than an empty string. Fails at config provisioning, before any ACME traffic.","triggerScenarios":"Setting email to a placeholder like {env.ACME_EMAIL} in the tls/ACME issuer config when the environment variable is not set in Caddy's process, or using placeholder syntax the replacer cannot resolve (typo, unsupported key, unclosed braces).","commonSituations":"Env var name mismatch (ACME_EMAIL vs CERT_EMAIL); .env files loaded by a wrapper but not by the systemd unit running Caddy; container env vars set in one service but not the one running Caddy; typos like {env.EMAIL}}.","solutions":["Set the missing variable in the environment Caddy actually runs in (systemctl edit caddy -> Environment=..., or the container env) and restart","Fix the placeholder syntax: exactly {env.VAR_NAME} with matching braces","If the value is not secret-dependent, hardcode the email or use the ACME_EMAIL/admin email defaults and drop the placeholder","Test with caddy adapt + caddy validate --adapter caddyfile to catch it before reload"],"exampleFix":"# before\n example.com {\n   tls {\n     email {env.ACME_MAIL}   # typo: variable is ACME_EMAIL\n   }\n }\n\n# after\n example.com {\n   tls {\n     email {env.ACME_EMAIL}\n   }\n }\n# and: systemctl edit caddy -> [Service] Environment=ACME_EMAIL=me@example.com","handlingStrategy":"validation","validationCode":"// expose placeholder problems before Caddy loads the config\nimport \"os\"\n\nfunc envPlaceholdersSet(cfg string) error {\n\tre := regexp.MustCompile(`\\{env\\.([A-Za-z0-9_]+)}`)\n\tfor _, m := range re.FindAllStringSubmatch(cfg, -1) {\n\t\tif os.Getenv(m[1]) == \"\" {\n\t\t\treturn fmt.Errorf(\"placeholder {env.%s} is not set\", m[1])\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := issuer.Provision(ctx); err != nil {\n    if strings.Contains(err.Error(), \"expanding email address\") {\n        // unset env var or bad placeholder: fix environment, reload config\n    }\n    return err\n}","preventionTips":["Run caddy validate with the production environment (EnvironmentFile) as part of deploy","Centralize env vars in one EnvironmentFile with documented keys","Prefer {env.VAR} consistently and grep configs for undefined vars in CI","Avoid placeholders for values you can set directly (admin email global option)"],"tags":["tls","acme","placeholders","environment","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}