BerriAI/litellm · error · OCIError
Each content item must have a string `type` field
Error message
Each content item must have a string `type` field
What it means
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.
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)
Example fix
# before
content = [{"text": "hello"}] # no `type`
# after
content = [{"type": "text", "text": "hello"}] Defensive patterns
Strategy: type-guard
Validate before calling
def ensure_content_part_types(content: list) -> list:
for part in content:
if not isinstance(part.get("type"), str):
raise ValueError(f"content part missing string `type`: {part!r}")
return content Type guard
def is_valid_content_part(part) -> bool:
return isinstance(part, dict) and isinstance(part.get("type"), str) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Content item of type `text` must have a string `text` field
- Content type ` ` is not supported by OCI
- Each content item must be a dictionary
- Each tool call must be a dictionary
- Message `content` must be a string or list of content parts
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/27e4abc2c6d9a100.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/llms/oci/chat/generic.py:76
def adapt_messages_to_generic_oci_standard_content_message(role: str, content: str | list) -> OCIMessage:
"""Convert a plain-text or multipart content message to OCI format."""
new_content: Final[list[OCIContentPartUnion]] = []
if isinstance(content, str):
return OCIMessage(
role=open_ai_to_generic_oci_role_map[role],
content=[OCITextContentPart(text=content)],
toolCalls=None,
toolCallId=None,
)
for content_item in content:
if not isinstance(content_item, dict):
raise OCIError(status_code=400, message="Each content item must be a dictionary")
item_type = content_item.get("type")
if not isinstance(item_type, str):
raise OCIError(
status_code=400,
message="Each content item must have a string `type` field",
)
if item_type not in ["text", "image_url"]:
raise OCIError(
status_code=400,
message=f"Content type `{item_type}` is not supported by OCI",
)
if item_type == "text":
text = content_item.get("text")
if not isinstance(text, str):
raise OCIError(
status_code=400,
message="Content item of type `text` must have a string `text` field",
)
new_content.append(OCITextContentPart(text=text))
View on GitHub (pinned to 77b7c6c40c)