caddyserver/caddy · error
module %s (%T) is not a listener wrapper
Error message
module %s (%T) is not a listener wrapper
What it means
In a server options block ('servers { listener_wrappers { ... } }'), the named module under 'caddy.listeners.' is unmarshaled via caddyfile.UnmarshalModule and then asserted to implement caddy.ListenerWrapper. If the assertion fails, the module cannot wrap the listener and adapt aborts. Typical cause: a plugin built for an incompatible Caddy version or a module registered in that namespace without implementing the wrapper interface.
Source
Thrown at caddyconfig/httpcaddyfile/serveroptions.go:97
case "name":
if serverOpts.ListenerAddress == "" {
return nil, d.Errf("cannot set a name for a server without a listener address")
}
if !d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.Name = d.Val()
case "listener_wrappers":
for nesting := d.Nesting(); d.NextBlock(nesting); {
modID := "caddy.listeners." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
listenerWrapper, ok := unm.(caddy.ListenerWrapper)
if !ok {
return nil, fmt.Errorf("module %s (%T) is not a listener wrapper", modID, unm)
}
jsonListenerWrapper := caddyconfig.JSONModuleObject(
listenerWrapper,
"wrapper",
listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
nil,
)
serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
}
case "packet_conn_wrappers":
for nesting := d.Nesting(); d.NextBlock(nesting); {
modID := "caddy.packetconns." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
packetConnWrapper, ok := unm.(caddy.PacketConnWrapper)View on GitHub (pinned to 50e54ee279)
Solutions
- Confirm the module exists and its namespace: 'caddy list-modules | grep caddy.listeners'.
- Rebuild with xcaddy including the wrapper plugin at a version matching core.
- If the plugin is yours, implement caddy.ListenerWrapper (WrapListener) and add an interface guard.
Example fix
# before: plugin missing from this build
{
servers {
listener_wrappers {
proxy_protocol
}
}
}
# after: build with the plugin, then the same config works
# xcaddy build --with github.com/mastercactapult/caddy-proxyprotocol Defensive patterns
Strategy: validation
Validate before calling
caddy list-modules | grep '^caddy.listeners.' # confirm the wrapper exists in this build
Type guard
var _ caddy.ListenerWrapper = (*MyWrapper)(nil)
Prevention
- Check module availability with caddy list-modules before writing server options for custom builds.
- Keep xcaddy build recipes in version control so listener wrapper plugins are never forgotten.
When it happens
Trigger: A listener-wrapper plugin registered under caddy.listeners that lacks the WrapListener method; a plugin compiled against an older Caddy where the ListenerWrapper interface differed; version skew between core and plugin after an upgrade.
Common situations: Configuring 'listener_wrappers { proxy_protocol }' with a plugin not rebuilt for the current core; upgrading Caddy core without rebuilding listener plugins; copy-pasting server options from a different distribution's docs.
Related errors
- module %s (%T) is not a packet conn wrapper
- matcher module '%s' is not a Caddyfile unmarshaler
- matcher module '%s' is not a request matcher
- marshaling matcher set %#v: %v
- module %s (%T) is not an IP range source
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/2fa14a54f8ab478e.
Report an issue: GitHub.