mem0ai/mem0 · error · ValueError

headers must be a dictionary

Error message

headers must be a dictionary

What it means

Raised by the Elasticsearch config's header validator when the 'headers' option is provided but is not a dict. Mem0 passes custom HTTP headers to the underlying Elasticsearch client, which requires a mapping of header name to value; strings, lists, or tuples are rejected at config validation time.

Source

Thrown at mem0/configs/vector_stores/elasticsearch.py:46

        # Check if either cloud_id or host/port is provided
        if not values.get("cloud_id") and not values.get("host"):
            raise ValueError("Either cloud_id or host must be provided")

        # Check if authentication is provided
        if not any([values.get("api_key"), (values.get("user") and values.get("password"))]):
            raise ValueError("Either api_key or user/password must be provided")

        return values

    @model_validator(mode="before")
    @classmethod
    def validate_headers(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        """Validate headers format and content"""
        headers = values.get("headers")
        if headers is not None:
            # Check if headers is a dictionary
            if not isinstance(headers, dict):
                raise ValueError("headers must be a dictionary")
            
            # Check if all keys and values are strings
            for key, value in headers.items():
                if not isinstance(key, str) or not isinstance(value, str):
                    raise ValueError("All header keys and values must be strings")
        
        return values

    @model_validator(mode="before")
    @classmethod
    def validate_extra_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        allowed_fields = set(cls.model_fields.keys())
        input_fields = set(values.keys())
        extra_fields = input_fields - allowed_fields
        if extra_fields:
            raise ValueError(
                f"Extra fields not allowed: {', '.join(extra_fields)}. "
                f"Please input only the following fields: {', '.join(allowed_fields)}"

View on GitHub (pinned to 001c235229)

Solutions

  1. Provide headers as a dict: headers={"Authorization": "Bearer x"}
  2. If headers come from a list of 'k: v' strings, parse them into a dict before constructing the config
  3. Omit 'headers' entirely when no custom headers are needed
  4. Add a unit assertion that headers is a dict in config-loading code

Example fix

# before
ElasticsearchConfig(host="h", api_key="k", headers="X-Opaque-Id: 42")

# after
ElasticsearchConfig(host="h", api_key="k", headers={"X-Opaque-Id": "42"})
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_es_headers(cfg: dict) -> None:
    h = cfg.get("headers")
    if h is not None and not isinstance(h, dict):
        raise RuntimeError("'headers' must be a dict of name -> value")

Type guard

def headers_is_dict(cfg: dict) -> bool:
    h = cfg.get("headers")
    return h is None or isinstance(h, dict)

Try / catch

from pydantic import ValidationError
try:
    ElasticsearchConfig(**cfg)
except ValidationError as e:
    if "headers must be a dictionary" in str(e):
        # coerce/parse headers into a dict, then retry
        ...

Prevention

When it happens

Trigger: Passing headers as anything other than a dict, e.g. headers='Authorization: Bearer x' (a string), headers=[('x','y')] (list of tuples), or headers=None wrapped in a list by a config loader.

Common situations: Translating curl-style '-H' header strings directly into config; JSON/YAML configs where headers collapse to a string; programmatic config builders defaulting headers to a serialized form.

Related errors


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/e2eada62a3097457. Report an issue: GitHub.