caddyserver/caddy · error
loading listener wrapper modules: %v
Error message
loading listener wrapper modules: %v
What it means
ctx.LoadModule deserializes and instantiates each module in servers.<name>.listener_wrappers. If the raw JSON for any wrapper is not a registered caddy.ListenerWrapper module, or the module's own provisioning fails, the whole app fails to provision. The %v is the underlying module-loading error.
Source
Thrown at modules/caddyhttp/app.go:326
}
if err := srv.provisionDotHeaders(); err != nil {
return fmt.Errorf("server %s: %v", srvName, err)
}
// process each listener address
for i := range srv.Listen {
lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
if err != nil {
return fmt.Errorf("server %s, listener %d: %v", srvName, i, err)
}
srv.Listen[i] = lnOut
}
// set up each listener modifier
if srv.ListenerWrappersRaw != nil {
vals, err := ctx.LoadModule(srv, "ListenerWrappersRaw")
if err != nil {
return fmt.Errorf("loading listener wrapper modules: %v", err)
}
var hasTLSPlaceholder bool
for i, val := range vals.([]any) {
if _, ok := val.(*tlsPlaceholderWrapper); ok {
if i == 0 {
// putting the tls placeholder wrapper first is nonsensical because
// that is the default, implicit setting: without it, all wrappers
// will go after the TLS listener anyway
return fmt.Errorf("it is unnecessary to specify the TLS listener wrapper in the first position because that is the default")
}
if hasTLSPlaceholder {
return fmt.Errorf("TLS listener wrapper can only be specified once")
}
hasTLSPlaceholder = true
}
srv.listenerWrappers = append(srv.listenerWrappers, val.(caddy.ListenerWrapper))
}
// if any wrappers were configured but the TLS placeholder wrapper isView on GitHub (pinned to 50e54ee279)
Solutions
- Read the wrapped error: it names the module or field that failed
- Ensure the wrapper plugin is compiled into your binary (xcaddy build with the plugin) or remove it from the config
- Verify the module ID matches the plugin docs exactly (e.g. caddy.listeners.*.*)
Example fix
// before
"listener_wrappers": [{"wrapper":"proxy_protocol"}] // plugin missing
// after
xcaddy build --with github.com/mholt/caddy-l4 # then keep config Defensive patterns
Strategy: validation
Validate before calling
// verify each listener wrapper module is registered in this build
ids := caddy.ModulesModuleIDs() // or `caddy list-modules` from CLI
for _, w := range srvCfg.ListenerWrappersRaw {
name := gjson.GetBytes(w, "wrapper").String()
if !slices.Contains(ids, "caddy.listeners."+name) && !slices.Contains(ids, name) {
return fmt.Errorf("listener wrapper %q not in build", name)
}
} Prevention
- Pin and document the exact xcaddy build (plugins + versions) next to the config
- Run `caddy list-modules` after rebuilding and diff against required modules
- Adapt+validate configs in the same container image that will run them
When it happens
Trigger: Referencing an unregistered listener wrapper module ID (e.g. from a plugin binary not compiled in), a typo in the module name, or a valid wrapper whose Provision returns an error (bad option values).
Common situations: Using a custom build (xcaddy) that omits a plugin referenced in JSON config; copying configs between Caddy builds with different plugin sets; option name changes across plugin versions.
Related errors
- loading storage module: %v
- loading packet conn wrapper modules: %v
- invalid listener address '%s': %v
- server %s: listener address repeated: %s (already claimed by
- --config is required
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/b3994103570491da.
Report an issue: GitHub.