{"record":{"id":"b77d6be5f3e1f0e6","repo":"FoundationAgents/MetaGPT","slug":"headers-must-be-a-dictionary","errorCode":null,"errorMessage":"Headers must be a dictionary","messagePattern":"Headers must be a dictionary","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"metagpt/provider/general_api_base.py","lineNumber":427,"sourceCode":"\n        if self.organization:\n            headers[\"LLM-Organization\"] = self.organization\n\n        if self.api_version is not None and self.api_type == ApiType.OPEN_AI:\n            headers[\"LLM-Version\"] = self.api_version\n        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,","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/FoundationAgents/MetaGPT/blob/11cdf466d042aece04fc6cfd13b28e1a70341b1f/metagpt/provider/general_api_base.py#L409-L445","documentation":"Raised by OpenAIRequestor._validate_headers when default_headers supplied to a request is not a Python dict. The requestor validates all caller-supplied headers before merging them with auth headers, so a non-mapping value (list, string, None-like sentinel object, tuple) fails immediately with this TypeError.","triggerScenarios":"Passing default_headers as anything but a dict/None when constructing or invoking a provider request, e.g. default_headers=\"[{\\\"k\\\":\"v\\\"}]\" (a JSON string), a list of tuples, or a requests-compatible Headers object that is not a dict subclass.","commonSituations":"Deserializing headers from JSON/YAML without json.loads, piping headers from a CLI argument as a string, or reusing a config structure that stores headers as a list of pairs.","solutions":["Pass default_headers as a plain dict of strings, e.g. {\"X-Org-Id\": \"42\"}, or omit/None it.","If headers arrive as a JSON string, parse with json.loads first: default_headers=json.loads(raw).","If headers are a list of (k, v) pairs, convert with dict(pairs).","Ensure custom provider subclasses do not override default_headers with a non-dict attribute."],"exampleFix":"# before\nheaders = '[{\"X-Tenant\": \"acme\"}]'  # str -> TypeError\nreq.request(..., supplied_headers=headers)\n\n# after\nheaders = {\"X-Tenant\": \"acme\"}\nreq.request(..., supplied_headers=headers)","handlingStrategy":"type-guard","validationCode":"def normalize_headers(h):\n    if h is None:\n        return {}\n    if isinstance(h, str):\n        import json; h = json.loads(h)\n    if isinstance(h, (list, tuple)):\n        h = dict(h)\n    if not isinstance(h, dict):\n        raise TypeError(f\"headers must be dict, got {type(h).__name__}\")\n    return {str(k): str(v) for k, v in h.items()}\n\nsupplied = normalize_headers(raw_headers)","typeGuard":"def is_valid_headers(h) -> TypeGuard[Optional[Dict[str, str]]]:\n    return h is None or (isinstance(h, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in h.items()))","tryCatchPattern":"try:\n    requestor._validate_headers(supplied)\nexcept TypeError as e:\n    raise ValueError(f\"Bad headers payload: {e}; pass dict[str, str]\") from e","preventionTips":["Always json.loads header strings from configs or CLI","Normalize headers through one helper before any request","Type default_headers as Optional[Dict[str, str]] in your config models"],"tags":["http-headers","type-error","openai","validation"],"backgroundTag":null,"analyzedSha":"11cdf466d042aece04fc6cfd13b28e1a70341b1f","analyzedAt":"2026-08-14T23:20:02.994Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}