BerriAI/litellm · error · OCIError
Each content item must be a dictionary
Error message
Each content item must be a dictionary
What it means
When converting an OpenAI-format message to OCI GENERIC format, multipart content must be a list of dictionaries (content parts). If any element of the content list is not a dict (e.g. a raw string, a Pydantic object, or None), OCIError(400) 'Each content item must be a dictionary' is raised before any request is sent. This is strict client-side validation of caller input.
Source
Thrown at litellm/llms/oci/chat/generic.py:72
# ---------------------------------------------------------------------------
# Message building
# ---------------------------------------------------------------------------
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,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Make every element of the content list a dict with a 'type' key, e.g. {'type':'text','text':'...'} — hoist stray strings into {'type':'text','text':<string>}.
- If the whole content is plain text, pass it as a plain string instead of a list.
- Normalize messages from external frameworks through json.loads(json.dumps(...)) or an explicit mapper before calling litellm.
Example fix
# before
messages=[{'role':'user','content':['Describe this', {'type':'image_url','image_url':{'url':u}}]}]
# after
messages=[{'role':'user','content':[{'type':'text','text':'Describe this'}, {'type':'image_url','image_url':{'url':u}}]}] Defensive patterns
Strategy: validation
Validate before calling
def normalize_content(content):
if isinstance(content, str):
return content
parts = []
for item in content:
if isinstance(item, str): # hoist stray strings
item = {'type': 'text', 'text': item}
assert isinstance(item, dict), f'content part must be dict, got {type(item)}'
parts.append(item)
return parts
messages = [{'role': 'user', 'content': normalize_content(raw_content)}] Type guard
def is_valid_oci_content(content) -> bool:
if isinstance(content, str):
return True
return isinstance(content, list) and all(isinstance(p, dict) for p in content) Try / catch
from litellm.llms.oci.common_utils import OCIError
try:
litellm.completion(model='oci/meta.llama-3.3-70b-instruct', messages=msgs)
except OCIError as e:
if 'content item must be a dictionary' in str(e):
msgs = repair_content_parts(msgs) # your normalizer
raise Prevention
- Build content parts through one helper so shape is uniform everywhere.
- json.loads(json.dumps(parts)) when parts come from external frameworks to force plain dicts.
- Unit-test the message builder against the OCI validation rules.
When it happens
Trigger: Calling completion with an oci/ GENERIC model (llama, grok, gemini on OCI) with content like [{'type':'text','text':'hi'}, 'plain string'] or content parts produced by another SDK as objects/NamedTuples rather than plain dicts.
Common situations: Mixing a plain string into a content-parts list by accident (list concatenation bug); passing messages serialized from a framework that emits non-dict part objects; copy-pasting multimodal examples that use a different part representation.
Related errors
- Each content item must have a string `type` field
- Each tool call must be a dictionary
- Message `content` must be a string or list of content parts
- Content type `{item_type}` is not supported by OCI
- Content item of type `text` must have a string `text` field
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/f52120bf76b1db22.
Report an issue: GitHub.