FoundationAgents/MetaGPT · error · TypeError

Header keys must be strings

Error message

Header keys must be strings

What it means

Raised inside _validate_headers while iterating supplied headers: the container is a dict, but at least one key is not a str. Header keys become HTTP header names, so non-string keys (int, tuple, bytes) are rejected client-side before the request is sent.

Source

Thrown at metagpt/provider/general_api_base.py:431

        if self.api_version is not None and self.api_type == ApiType.OPEN_AI:
            headers["LLM-Version"] = self.api_version
        if request_id is not None:
            headers["X-Request-Id"] = request_id
        headers.update(extra)

        return headers

    def _validate_headers(self, supplied_headers: Optional[Dict[str, str]]) -> Dict[str, str]:
        headers: Dict[str, str] = {}
        if supplied_headers is None:
            return headers

        if not isinstance(supplied_headers, dict):
            raise TypeError("Headers must be a dictionary")

        for k, v in supplied_headers.items():
            if not isinstance(k, str):
                raise TypeError("Header keys must be strings")
            if not isinstance(v, str):
                raise TypeError("Header values must be strings")
            headers[k] = v

        # NOTE: It is possible to do more validation of the headers, but a request could always
        # be made to the API manually with invalid headers, so we need to handle them server side.

        return headers

    def _prepare_request_raw(
        self,
        url,
        supplied_headers,
        method,
        params,
        files,
        request_id: Optional[str],
    ) -> Tuple[str, Dict[str, str], Optional[bytes]]:

View on GitHub (pinned to 11cdf466d0)

Solutions

  1. Convert all header keys to strings: {str(k): v for k, v in headers.items()}.
  2. For bytes keys, decode first: {k.decode(): v for k, v in headers.items()}.
  3. Replace numeric or tuple keys with the intended header names (e.g. "X-Request-Id").
  4. Add a startup assertion that all(isinstance(k, str) for k in headers).

Example fix

# before
headers = {1: "application/json"}  # int key -> TypeError

# after
headers = {"1": "application/json"}  # or {str(k): v for k, v in headers.items()}
Defensive patterns

Strategy: type-guard

Validate before calling

headers = {str(k): v for k, v in raw_headers.items()}
assert all(isinstance(k, str) for k in headers)

Type guard

def has_string_keys(h: Dict[Any, Any]) -> TypeGuard[Dict[str, Any]]:
    return isinstance(h, dict) and all(isinstance(k, str) for k in h)

Try / catch

try:
    validated = requestor._validate_headers(headers)
except TypeError:
    headers = {str(k): str(v) for k, v in headers.items()}
    validated = requestor._validate_headers(headers)

Prevention

When it happens

Trigger: Passing a dict with non-string keys such as {8080: "true"}, {("a", "b"): "v"}, or {b"X-Trace": "1"} as default_headers/supplied_headers to any request through the general API base.

Common situations: Building headers dynamically from numeric IDs or enum values without str() conversion, mixing Python 2-style bytes keys into a ported codebase, or using dict(zip(range(...), values)) by mistake.

Related errors


AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14). Data as JSON: /api/errors/3e0fcf823ac6c1c8. Report an issue: GitHub.