crowdsecurity/crowdsec · error

invalid type for headers: %T

Error message

invalid type for headers: %T

What it means

HTTPRequest is an expr-lang helper taking (method, url, headers map[string]any, body string). The headers argument must be a map[string]any (typically an expr map literal); any other Go type — map[string]string, nil, or a non-map — is rejected with this error before any request is made.

Source

Thrown at pkg/exprhelpers/http.go:128

}

// HTTPRequest(method string, url string, headers map[string]any, body string) (*HTTPResponse, error)
func HTTPRequest(params ...any) (any, error) {
	method, ok := params[0].(string)
	if !ok {
		return nil, fmt.Errorf("invalid type for method: %T", params[0])
	}

	uri, ok := params[1].(string)
	if !ok {
		return nil, fmt.Errorf("invalid type for url: %T", params[1])
	}

	// headers is map[string]any so that expr map literals (map[string]interface{})
	// are accepted; values are stringified.
	rawHeaders, ok := params[2].(map[string]any)
	if !ok {
		return nil, fmt.Errorf("invalid type for headers: %T", params[2])
	}

	headers := make(map[string]string, len(rawHeaders))
	for k, v := range rawHeaders {
		headers[k] = fmt.Sprint(v)
	}

	body, ok := params[3].(string)
	if !ok {
		return nil, fmt.Errorf("invalid type for body: %T", params[3])
	}

	var reader io.Reader
	if body != "" {
		reader = strings.NewReader(body)
	}

	return doHTTPRequest(method, uri, headers, reader)

View on GitHub (pinned to 909b515798)

Solutions

  1. Pass headers as an expr map literal like {"Content-Type": "application/json"}, which produces map[string]any
  2. If you have a map[string]string, rebuild it as map[string]any before calling HTTPRequest
  3. Ensure all four arguments (method, url, headers, body) are provided so headers isn't a missing/nil param
  4. Wrap header construction so non-map values are converted or defaulted to an empty map

Example fix

// before (map[string]string rejected)
HTTPRequest("POST", url, map[string]string{"X-Token": token}, "")
// after
HTTPRequest("POST", url, {"X-Token": token}, "")
Defensive patterns

Strategy: type-guard

Validate before calling

hdrs, ok := args[2].(map[string]any)
if !ok {
    return fmt.Errorf("headers must be a map[string]any, got %T", args[2])
}

Type guard

func isHeaderMap(v any) bool {
    _, ok := v.(map[string]any)
    return ok
}

Try / catch

resp, err := exprhelpers.HTTPRequest(method, url, headers, body)
if err != nil {
    if strings.HasPrefix(err.Error(), "invalid type for headers") {
        // fall back to default headers or skip request
    }
    return err
}

Prevention

When it happens

Trigger: Calling HTTPRequest with params[2] not of type map[string]any: passing a map[string]string literal, passing nil as headers, or passing an expr expression that evaluates to something other than a map (e.g. a slice or struct).

Common situations: Writing expr helper calls by hand and supplying headers as map[string]string (a common Go habit), forgetting the headers argument so nil is passed, or piping a parsed JSON object whose dynamic type isn't map[string]any.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/513ffa8f08bd6cb0. Report an issue: GitHub.