{"record":{"id":"9f99d405d3b45281","repo":"FoundationAgents/MetaGPT","slug":"header-values-must-be-strings","errorCode":null,"errorMessage":"Header values must be strings","messagePattern":"Header values must be strings","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"metagpt/provider/general_api_base.py","lineNumber":433,"sourceCode":"        if request_id is not None:\n            headers[\"X-Request-Id\"] = request_id\n        headers.update(extra)\n\n        return headers\n\n    def _validate_headers(self, supplied_headers: Optional[Dict[str, str]]) -> Dict[str, str]:\n        headers: Dict[str, str] = {}\n        if supplied_headers is None:\n            return headers\n\n        if not isinstance(supplied_headers, dict):\n            raise TypeError(\"Headers must be a dictionary\")\n\n        for k, v in supplied_headers.items():\n            if not isinstance(k, str):\n                raise TypeError(\"Header keys must be strings\")\n            if not isinstance(v, str):\n                raise TypeError(\"Header values must be strings\")\n            headers[k] = v\n\n        # NOTE: It is possible to do more validation of the headers, but a request could always\n        # be made to the API manually with invalid headers, so we need to handle them server side.\n\n        return headers\n\n    def _prepare_request_raw(\n        self,\n        url,\n        supplied_headers,\n        method,\n        params,\n        files,\n        request_id: Optional[str],\n    ) -> Tuple[str, Dict[str, str], Optional[bytes]]:\n        abs_url = \"%s%s\" % (self.base_url, url)\n        headers = self._validate_headers(supplied_headers)","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/FoundationAgents/MetaGPT/blob/11cdf466d042aece04fc6cfd13b28e1a70341b1f/metagpt/provider/general_api_base.py#L415-L451","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Stringify every value: {k: str(v) for k, v in headers.items()}.","For booleans use \"true\"/\"false\" strings explicitly instead of YAML bare true/false.","Drop or default None values: {k: v for k, v in headers.items() if v is not None}.","Quote numeric header values in YAML so they load as strings."],"exampleFix":"# before (config2.yaml)\ndefault_headers:\n  X-Temperature: 0.7   # float -> TypeError\n\n# after\ndefault_headers:\n  X-Temperature: \"0.7\"","handlingStrategy":"type-guard","validationCode":"headers = {k: (\"\" if v is None else str(v)) for k, v in raw_headers.items()}\nassert all(isinstance(v, str) for v in headers.values())","typeGuard":"def has_string_values(h: Dict[str, Any]) -> TypeGuard[Dict[str, str]]:\n    return isinstance(h, dict) and all(isinstance(v, str) for v in h.values())","tryCatchPattern":"try:\n    validated = requestor._validate_headers(headers)\nexcept TypeError as e:\n    if \"values\" in str(e):\n        headers = {k: str(v) for k, v in headers.items()}\n        validated = requestor._validate_headers(headers)\n    else:\n        raise","preventionTips":["Quote all header values in YAML","Replace None header values with empty string or drop the key","Cast bools to \"true\"/\"false\" strings explicitly"],"tags":["http-headers","type-error","yaml","validation"],"backgroundTag":null,"analyzedSha":"11cdf466d042aece04fc6cfd13b28e1a70341b1f","analyzedAt":"2026-08-14T23:20:02.994Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}