caddyserver/caddy · error
marshaling %T matcher: %v
Error message
marshaling %T matcher: %v
What it means
After a connection matcher is parsed from a Caddyfile, Caddy json.Marshal's it to store in the ModuleMap of the connection policy; if marshaling fails (unexported fields with no JSON tags, unsupported types like func or chan in the matcher struct), this error wraps the json error.
Source
Thrown at modules/caddytls/connpolicy.go:1125
dd := caddyfile.NewDispenser(tokens)
dd.Next() // consume wrapper name
unm, err := caddyfile.UnmarshalModule(dd, "tls.handshake_match."+matcherName)
if err != nil {
return nil, err
}
cm, ok := unm.(ConnectionMatcher)
if !ok {
return nil, fmt.Errorf("matcher module '%s' is not a connection matcher", matcherName)
}
matcherMap[matcherName] = cm
}
matcherSet := make(caddy.ModuleMap)
for name, matcher := range matcherMap {
jsonBytes, err := json.Marshal(matcher)
if err != nil {
return nil, fmt.Errorf("marshaling %T matcher: %v", matcher, err)
}
matcherSet[name] = jsonBytes
}
return matcherSet, nil
}
View on GitHub (pinned to 50e54ee279)
Solutions
- If this is your plugin, ensure all config fields are exported and JSON-marshalable; keep runtime-only state in separate unexported-but-ignored fields (add json:"-" where needed)
- Rebuild the plugin against the deployed Caddy version
- If stock Caddy hits this, report it — it indicates a regression in a built-in matcher
Defensive patterns
Strategy: validation
Validate before calling
// Plugin authors: unit-test that matcher config marshals cleanly
func TestMatcherMarshals(t *testing.T) {
b, err := json.Marshal(&MyMatcher{ /* exported config fields */ })
if err != nil {
t.Fatalf("matcher not JSON-safe: %v", err)
}
t.Log(string(b))
} Prevention
- Keep matcher structs to exported, json-friendly fields; tag runtime state with json:"-"
- Never embed funcs/channels/locks in config-carrying structs
- Run go test on plugins in CI alongside caddy validate
When it happens
Trigger: Almost exclusively with third-party matcher modules whose structs contain unmarshalable fields; stock matchers marshal fine.
Common situations: Custom handshake matcher plugins during development; Go json rejecting types such as sync.Mutex, channels, or unexported complex fields.
Related errors
- 'ca' module '%s' is not a certificate pool provider
- leaf module '%s' is not a leaf certificate loader
- matcher module '%s' is not a connection matcher
- recombining SNI matchers: %v
- position %d: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/e9d2101911117eca.
Report an issue: GitHub.