caddyserver/caddy · error

unsupported element type in matcher input list: %T

Error message

unsupported element type in matcher input list: %T

What it means

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.

Source

Thrown at modules/caddyhttp/celmatcher.go:721

			convVals := make([]string, len(val))
			for i, elem := range val {
				strVal, ok := elem.(types.String)
				if !ok {
					return nil, fmt.Errorf("unsupported value type in matcher input: %T", val)
				}
				convVals[i] = string(strVal)
			}
			mapStrListStr[k] = convVals
		case []any:
			convVals := make([]string, len(val))
			for i, elem := range val {
				switch e := elem.(type) {
				case string:
					convVals[i] = e
				case types.String:
					convVals[i] = string(e)
				default:
					return nil, fmt.Errorf("unsupported element type in matcher input list: %T", elem)
				}
			}
			mapStrListStr[k] = convVals
		default:
			return nil, fmt.Errorf("unsupported value type in matcher input: %T", val)
		}
	}
	return mapStrListStr, nil
}

// isCELStringExpr indicates whether the expression is a supported string expression
func isCELStringExpr(e ast.Expr) bool {
	return isCELStringLiteral(e) || isCELCaddyPlaceholderCall(e) || isCELConcatCall(e)
}

// isCELStringLiteral returns whether the expression is a CEL string literal.
func isCELStringLiteral(e ast.Expr) bool {
	switch e.Kind() {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Quote every element in the value list so it is a string
  2. In YAML, wrap values in quotes to prevent implicit typing ("true" not true)
  3. Fix the config generator to always emit string arrays

Example fix

# before (YAML)
match:
  header:
    X-Flag: [true]
# after
match:
  header:
    X-Flag: ["true"]
Defensive patterns

Strategy: validation

Validate before calling

// pre-serialize check: all list elements are strings
for k, v := range headerMap {
    list, ok := v.([]any)
    if !ok { continue }
    for _, e := range list {
        if _, ok := e.(string); !ok {
            return fmt.Errorf("%s: non-string element %v", k, e)
        }
    }
}

Prevention

When it happens

Trigger: A map value list containing ints, floats, bools, or nil, e.g. {'header': [true]} passed through the CEL matcher conversion in celmatcher.go.

Common situations: Generated configs from templating systems where booleans/numbers leak into header value arrays; YAML configs where 'true' is parsed as a boolean.

Related errors


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