caddyserver/caddy · error
the 'forwarded' option is no longer supported; use the 'clie
Error message
the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead
What it means
Before publishing ECH configs, Caddy serializes the config list to its binary DNS wire format via echCfgList.MarshalBinary (the crypto/tls EncryptedClientHelloConfigList encoding). Marshal failed, meaning one of the in-memory ECH configs is malformed for wire encoding (bad config bytes, unsupported version, or an empty list edge case).
Source
Thrown at modules/caddyhttp/ip_matchers.go:121
// name of the macro, this is the function name that users see when writing expressions.
"remote_ip",
// name of the function that the macro will be rewritten to call.
"remote_ip_match_request_list",
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
func(data ref.Val) (RequestMatcherWithError, error) {
refStringList := stringSliceType
strList, err := data.ConvertToNative(refStringList)
if err != nil {
return nil, err
}
m := MatchRemoteIP{}
for _, input := range strList.([]string) {
if input == "forwarded" {
return nil, errors.New("the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead")
}
m.Ranges = append(m.Ranges, input)
}
err = m.Provision(ctx)
return m, err
},
)
}
// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
cidrs, zones, err := provisionCidrsZonesFromRanges(m.Ranges)
if err != nil {
return err
}
m.cidrs = cidrsView on GitHub (pinned to 50e54ee279)
Solutions
- Unwrap and inspect the MarshalBinary error for the offending config (often indicates a specific record length/version problem).
- Stop Caddy and reset the ech/configs storage folder so fresh, well-formed configs are generated, then let publication repopulate DNS records.
- After Caddy version upgrades with ECH enabled, expect rotation to replace old configs; if marshal fails immediately post-upgrade, reset state.
- Verify no external tooling writes into Caddy's storage.
Example fix
# before: publication fails on corrupt stored config # after: regenerate ECH state systemctl stop caddy && rm -rf /var/lib/caddy/ech/configs && systemctl start caddy
Defensive patterns
Strategy: fallback
Try / catch
On marshal failure of the config list, fall back to resetting ECH state: stop Caddy, back up and remove ech/configs, restart — fresh configs marshal cleanly and republish to DNS.
Prevention
- Reset stored ECH state when upgrading across versions that change the binary config format.
- Use atomic-write storage to prevent truncated config.bin.
- Validate configs after restore: a successful start plus one publication cycle confirms wire-format health.
When it happens
Trigger: tls.EncryptedClientHelloConfigList built from stored configs fails MarshalBinary: a config.bin entry loaded from storage is truncated/corrupt in a way that survived the load cleanup path, or a version/length field is inconsistent.
Common situations: Storage corruption of config.bin; a Caddy upgrade changing the expected binary layout while old configs remain in storage; hand-edited storage.
Related errors
- --config is required
- --input is required
- --output is required
- cannot reuse socket %v: unix socket is already in use by ano
- missing 'req' argument
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/74a6232a04ec806b.
Report an issue: GitHub.