{"record":{"id":"27e4abc2c6d9a100","repo":"BerriAI/litellm","slug":"each-content-item-must-have-a-string-type-field","errorCode":null,"errorMessage":"Each content item must have a string `type` field","messagePattern":"Each content item must have a string `type` field","errorType":"validation","errorClass":"OCIError","httpStatus":400,"severity":"error","filePath":"litellm/llms/oci/chat/generic.py","lineNumber":76,"sourceCode":"\ndef adapt_messages_to_generic_oci_standard_content_message(role: str, content: str | list) -> OCIMessage:\n    \"\"\"Convert a plain-text or multipart content message to OCI format.\"\"\"\n    new_content: Final[list[OCIContentPartUnion]] = []\n    if isinstance(content, str):\n        return OCIMessage(\n            role=open_ai_to_generic_oci_role_map[role],\n            content=[OCITextContentPart(text=content)],\n            toolCalls=None,\n            toolCallId=None,\n        )\n\n    for content_item in content:\n        if not isinstance(content_item, dict):\n            raise OCIError(status_code=400, message=\"Each content item must be a dictionary\")\n\n        item_type = content_item.get(\"type\")\n        if not isinstance(item_type, str):\n            raise OCIError(\n                status_code=400,\n                message=\"Each content item must have a string `type` field\",\n            )\n        if item_type not in [\"text\", \"image_url\"]:\n            raise OCIError(\n                status_code=400,\n                message=f\"Content type `{item_type}` is not supported by OCI\",\n            )\n\n        if item_type == \"text\":\n            text = content_item.get(\"text\")\n            if not isinstance(text, str):\n                raise OCIError(\n                    status_code=400,\n                    message=\"Content item of type `text` must have a string `text` field\",\n                )\n            new_content.append(OCITextContentPart(text=text))\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/oci/chat/generic.py#L58-L94","documentation":"During OCI GENERIC message translation, a multipart content item passed the dict check but its `type` field is absent or not a string. OpenAI content parts are discriminated by a string `type` (\"text\", \"image_url\"), and OCI needs that discriminator to map to its typed content parts, so LiteLLM rejects the message with a 400 before sending. Pure input-shape validation, fail-fast.","triggerScenarios":"litellm.completion with an oci/generic.* model and a message like {\"role\":\"user\",\"content\":[{\"text\":\"hi\"}]} (missing type) or {\"type\": 1, ...} (non-string type). No API call is made; the error is raised in adapt_messages_to_generic_oci_standard_content_message.","commonSituations":"Typos like \"Type\" or omitted type when hand-crafting parts; LLM-generated message JSON missing the discriminator; schemas/downstream code that strips the type key; porting from providers where type was optional.","solutions":["Add a string type to every content part: {\"type\": \"text\", \"text\": \"...\"} or {\"type\": \"image_url\", \"image_url\": {...}}","Check for casing/key mistakes (Type, content_type) in the code that builds messages","Add a pre-flight assertion/lint on message dicts before calling completion","If parts come from JSON deserialization, validate against a small schema (e.g. pydantic or the type guard below)"],"exampleFix":"# before\ncontent = [{\"text\": \"hello\"}]  # no `type`\n\n# after\ncontent = [{\"type\": \"text\", \"text\": \"hello\"}]","handlingStrategy":"type-guard","validationCode":"def ensure_content_part_types(content: list) -> list:\n    for part in content:\n        if not isinstance(part.get(\"type\"), str):\n            raise ValueError(f\"content part missing string `type`: {part!r}\")\n    return content","typeGuard":"def is_valid_content_part(part) -> bool:\n    return isinstance(part, dict) and isinstance(part.get(\"type\"), str)","tryCatchPattern":null,"preventionTips":["Build parts with a factory: text_part(text) / image_part(url) so `type` is never omitted","Validate deserialized message JSON against the OpenAI content-part shape before forwarding"],"tags":["oci","request-validation","content-parts","type-field"],"backgroundTag":"request-body-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}