{"record":{"id":"d34e775b9bf2c74d","repo":"caddyserver/caddy","slug":"unsupported-element-type-in-matcher-input-list-t","errorCode":null,"errorMessage":"unsupported element type in matcher input list: %T","messagePattern":"unsupported element type in matcher input list: %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/celmatcher.go","lineNumber":721,"sourceCode":"\t\t\tconvVals := make([]string, len(val))\n\t\t\tfor i, elem := range val {\n\t\t\t\tstrVal, ok := elem.(types.String)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unsupported value type in matcher input: %T\", val)\n\t\t\t\t}\n\t\t\t\tconvVals[i] = string(strVal)\n\t\t\t}\n\t\t\tmapStrListStr[k] = convVals\n\t\tcase []any:\n\t\t\tconvVals := make([]string, len(val))\n\t\t\tfor i, elem := range val {\n\t\t\t\tswitch e := elem.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tconvVals[i] = e\n\t\t\t\tcase types.String:\n\t\t\t\t\tconvVals[i] = string(e)\n\t\t\t\tdefault:\n\t\t\t\t\treturn nil, fmt.Errorf(\"unsupported element type in matcher input list: %T\", elem)\n\t\t\t\t}\n\t\t\t}\n\t\t\tmapStrListStr[k] = convVals\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported value type in matcher input: %T\", val)\n\t\t}\n\t}\n\treturn mapStrListStr, nil\n}\n\n// isCELStringExpr indicates whether the expression is a supported string expression\nfunc isCELStringExpr(e ast.Expr) bool {\n\treturn isCELStringLiteral(e) || isCELCaddyPlaceholderCall(e) || isCELConcatCall(e)\n}\n\n// isCELStringLiteral returns whether the expression is a CEL string literal.\nfunc isCELStringLiteral(e ast.Expr) bool {\n\tswitch e.Kind() {","sourceCodeStart":703,"sourceCodeEnd":739,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/celmatcher.go#L703-L739","documentation":"Similar to 381 but for the []any case: the matcher input map's list value contains an element that is neither a Go string nor a CEL types.String. The conversion loop hits the default branch and aborts with the element's Go type.","triggerScenarios":"A map value list containing ints, floats, bools, or nil, e.g. {'header': [true]} passed through the CEL matcher conversion in celmatcher.go.","commonSituations":"Generated configs from templating systems where booleans/numbers leak into header value arrays; YAML configs where 'true' is parsed as a boolean.","solutions":["Quote every element in the value list so it is a string","In YAML, wrap values in quotes to prevent implicit typing (\"true\" not true)","Fix the config generator to always emit string arrays"],"exampleFix":"# before (YAML)\nmatch:\n  header:\n    X-Flag: [true]\n# after\nmatch:\n  header:\n    X-Flag: [\"true\"]","handlingStrategy":"validation","validationCode":"// pre-serialize check: all list elements are strings\nfor k, v := range headerMap {\n    list, ok := v.([]any)\n    if !ok { continue }\n    for _, e := range list {\n        if _, ok := e.(string); !ok {\n            return fmt.Errorf(\"%s: non-string element %v\", k, e)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In YAML, quote values like \"true\"/\"42\" in header lists","Deduplicate config linting: fail on non-string header values"],"tags":["caddy","cel","matcher","type-conversion","yaml"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}