{"record":{"id":"23156854305ca09f","repo":"caddyserver/caddy","slug":"recombining-sni-matchers-v","errorCode":null,"errorMessage":"recombining SNI matchers: %v","messagePattern":"recombining SNI matchers: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httpcaddyfile/httptype.go","lineNumber":1236,"sourceCode":"\n\t\t\t// as a special case, if there are adjacent TLS conn policies that are identical except\n\t\t\t// by their matchers, and the matchers are specifically just ServerName (\"sni\") matchers\n\t\t\t// (by far the most common), we can combine them into a single policy\n\t\t\tif i == j-1 && len(cps[i].MatchersRaw) == 1 && len(cps[j].MatchersRaw) == 1 {\n\t\t\t\tif iSNIMatcherJSON, ok := cps[i].MatchersRaw[\"sni\"]; ok {\n\t\t\t\t\tif jSNIMatcherJSON, ok := cps[j].MatchersRaw[\"sni\"]; ok {\n\t\t\t\t\t\t// position of policies and the matcher criteria check out; if settings are\n\t\t\t\t\t\t// the same, then we can combine the policies; we have to unmarshal and\n\t\t\t\t\t\t// remarshal the matchers though\n\t\t\t\t\t\tif cps[i].SettingsEqual(*cps[j]) {\n\t\t\t\t\t\t\tvar iSNIMatcher caddytls.MatchServerName\n\t\t\t\t\t\t\tif err := json.Unmarshal(iSNIMatcherJSON, &iSNIMatcher); err == nil {\n\t\t\t\t\t\t\t\tvar jSNIMatcher caddytls.MatchServerName\n\t\t\t\t\t\t\t\tif err := json.Unmarshal(jSNIMatcherJSON, &jSNIMatcher); err == nil {\n\t\t\t\t\t\t\t\t\tiSNIMatcher = append(iSNIMatcher, jSNIMatcher...)\n\t\t\t\t\t\t\t\t\tcps[i].MatchersRaw[\"sni\"], err = json.Marshal(iSNIMatcher)\n\t\t\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\t\t\treturn nil, fmt.Errorf(\"recombining SNI matchers: %v\", err)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcps = slices.Delete(cps, j, j+1)\n\t\t\t\t\t\t\t\t\ti--\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if they have the same matcher, try to reconcile each field: either they must\n\t\t\t// be identical, or we have to be able to combine them safely\n\t\t\tif reflect.DeepEqual(cps[i].MatchersRaw, cps[j].MatchersRaw) {\n\t\t\t\tif len(cps[i].ALPN) > 0 &&\n\t\t\t\t\tlen(cps[j].ALPN) > 0 &&\n\t\t\t\t\t!reflect.DeepEqual(cps[i].ALPN, cps[j].ALPN) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"two policies with same match criteria have conflicting ALPN: %v vs. %v\",","sourceCodeStart":1218,"sourceCodeEnd":1254,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httpcaddyfile/httptype.go#L1218-L1254","documentation":"While consolidating connection policies, two policies with equal settings and positionally compatible SNI matchers were being merged by concatenating their MatchServerName lists; re-marshaling the combined list to JSON failed. This is essentially an internal serialization failure of data the adapter itself produced.","triggerScenarios":"consolidateConnPolicies finds cps[i] and cps[j] with SettingsEqual and mergeable SNI matchers, unmarshals both `sni` matcher JSON blobs into caddytls.MatchServerName, appends them, and json.Marshal of the combined slice errors — in practice only reachable if the MatchersRaw['sni'] payload is not what the adapter expects (e.g. third-party code injected malformed matcher JSON).","commonSituations":"Almost never seen with a stock Caddyfile; can occur when a plugin or custom adapter pre-populates or mutates connection policy MatchersRaw with invalid SNI matcher JSON before consolidation runs.","solutions":["If you use a custom adapter/plugin, ensure `sni` matchers in MatchersRaw are valid JSON arrays of strings","Update Caddy — internal marshaling bugs of this kind get fixed upstream","Reproduce with `caddy adapt --pretty` on a minimal config to identify which plugin mangles the matchers"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := json.Marshal(iSNIMatcher); err != nil {\n    // internal path: report upstream with config that reproduces it\n    return nil, fmt.Errorf(\"recombining SNI matchers: %w\", err)\n}","preventionTips":["Keep Caddy updated; this is an internal marshaling path","If injecting policy matchers programmatically, always emit []string JSON for sni","Add a unit test that round-trips MatchersRaw through Unmarshal/Marshal"],"tags":["caddy","tls","json","internal"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}