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

  1. Confirm the module exists and its namespace: 'caddy list-modules | grep caddy.listeners'.
  2. Rebuild with xcaddy including the wrapper plugin at a version matching core.
  3. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/2fa14a54f8ab478e. Report an issue: GitHub.