{"record":{"id":"e3c78c13ff2e22a1","repo":"caddyserver/caddy","slug":"server-s-listener-d-v","errorCode":null,"errorMessage":"server %s, listener %d: %v","messagePattern":"server (.+?), listener (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/app.go","lineNumber":317,"sourceCode":"\n\t\t// set the default client IP header to read from\n\t\tif srv.ClientIPHeaders == nil {\n\t\t\tsrv.ClientIPHeaders = []string{\"X-Forwarded-For\"}\n\t\t}\n\n\t\t// precompute underscore and dot header allowlist rules\n\t\tif err := srv.provisionUnderscoreHeaders(); err != nil {\n\t\t\treturn fmt.Errorf(\"server %s: %v\", srvName, err)\n\t\t}\n\t\tif err := srv.provisionDotHeaders(); err != nil {\n\t\t\treturn fmt.Errorf(\"server %s: %v\", srvName, err)\n\t\t}\n\n\t\t// process each listener address\n\t\tfor i := range srv.Listen {\n\t\t\tlnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"server %s, listener %d: %v\", srvName, i, err)\n\t\t\t}\n\t\t\tsrv.Listen[i] = lnOut\n\t\t}\n\n\t\t// set up each listener modifier\n\t\tif srv.ListenerWrappersRaw != nil {\n\t\t\tvals, err := ctx.LoadModule(srv, \"ListenerWrappersRaw\")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"loading listener wrapper modules: %v\", err)\n\t\t\t}\n\t\t\tvar hasTLSPlaceholder bool\n\t\t\tfor i, val := range vals.([]any) {\n\t\t\t\tif _, ok := val.(*tlsPlaceholderWrapper); ok {\n\t\t\t\t\tif i == 0 {\n\t\t\t\t\t\t// putting the tls placeholder wrapper first is nonsensical because\n\t\t\t\t\t\t// that is the default, implicit setting: without it, all wrappers\n\t\t\t\t\t\t// will go after the TLS listener anyway\n\t\t\t\t\t\treturn fmt.Errorf(\"it is unnecessary to specify the TLS listener wrapper in the first position because that is the default\")","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/app.go#L299-L335","documentation":"Caddy expands {placeholders} in each servers.<name>.listen address during provisioning (repl.ReplaceOrErr with errOnEmpty/errOnUnknown). If a placeholder is unknown or resolves to an empty string, provisioning of that server fails, naming the server and the listener index. Only the first bad listener is reported.","triggerScenarios":"A listen address containing an unset environment placeholder like \"{env.LISTEN_ADDR}\", a malformed placeholder such as \"{env.LISTEN_ADDR\" (missing brace), or a placeholder whose value is empty at load time.","commonSituations":"Deploying a templated config where LISTEN_ADDR/HOST/PORT env vars are not exported in the service unit; typos in placeholder names; systemd/Docker env not passed to the Caddy process.","solutions":["Check that every env var referenced by {env.*} in listen addresses is set for the Caddy process (print env inside the same unit)","Fix placeholder syntax: balanced braces, correct namespace (env., service., etc.)","Give the placeholder a safe literal default instead of relying on it being set"],"exampleFix":"// before\n\"listen\": [\"{env.BIND_ADDR}:443\"]  // BIND_ADDR unset\n// after\n\"listen\": [\"{$BIND_ADDR:localhost}:443\"]  // with default","handlingStrategy":"validation","validationCode":"// fail fast before caddy.Load if env placeholders are used in listen\nfor _, l := range srvCfg.Listen {\n    if strings.Contains(l, \"{env.\") {\n        for _, m := range regexp.MustCompile(`\\{env\\.([A-Za-z0-9_]+)\\}`).FindAllStringSubmatch(l, -1) {\n            if os.Getenv(m[1]) == \"\" {\n                return fmt.Errorf(\"env %s needed by listen %q is unset\", m[1], l)\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := caddy.Load(cfgJSON, true); err != nil { /* message includes server+listener index; fix placeholder source */ }","preventionTips":["Prefer Caddyfile `{$VAR:default}` env syntax with defaults over JSON {env.*} in listen","Export all env vars in the systemd unit / container before ExecStart","Use `caddy adapt --pretty` to inspect what placeholders remain unresolved"],"tags":["caddy","placeholders","listener","provisioning","environment"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}