FoundationAgents/MetaGPT · error · TypeError

Header values must be strings

Error message

Header values must be strings

What it means

Companion check in _validate_headers: the dict's keys are strings, but at least one value is not. HTTP header values must be strings, so ints, bools, None, lists, or dicts as values raise this TypeError before the request goes out.

Source

Thrown at metagpt/provider/general_api_base.py:433

        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]]:
        abs_url = "%s%s" % (self.base_url, url)
        headers = self._validate_headers(supplied_headers)

View on GitHub (pinned to 11cdf466d0)

Solutions

  1. Stringify every value: {k: str(v) for k, v in headers.items()}.
  2. For booleans use "true"/"false" strings explicitly instead of YAML bare true/false.
  3. Drop or default None values: {k: v for k, v in headers.items() if v is not None}.
  4. Quote numeric header values in YAML so they load as strings.

Example fix

# before (config2.yaml)
default_headers:
  X-Temperature: 0.7   # float -> TypeError

# after
default_headers:
  X-Temperature: "0.7"
Defensive patterns

Strategy: type-guard

Validate before calling

headers = {k: ("" if v is None else str(v)) for k, v in raw_headers.items()}
assert all(isinstance(v, str) for v in headers.values())

Type guard

def has_string_values(h: Dict[str, Any]) -> TypeGuard[Dict[str, str]]:
    return isinstance(h, dict) and all(isinstance(v, str) for v in h.values())

Try / catch

try:
    validated = requestor._validate_headers(headers)
except TypeError as e:
    if "values" in str(e):
        headers = {k: str(v) for k, v in headers.items()}
        validated = requestor._validate_headers(headers)
    else:
        raise

Prevention

When it happens

Trigger: Passing headers like {"X-Max-Retries": 3}, {"X-Debug": True}, or {"Authorization": None} to a provider request; any non-str value triggers the raise even if other values are fine.

Common situations: Config-driven headers where YAML auto-types values (true -> bool, 42 -> int), header templates with unfilled placeholders set to None, or passing a token read as bytes instead of str.

Related errors


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