BerriAI/litellm · error · OCIError
Tool call `function` must be a dictionary
Error message
Tool call `function` must be a dictionary
What it means
Inside each tool call dict, the 'function' member must itself be a dictionary containing the tool name and arguments. If 'function' is missing, None, a string, or any non-dict value, OCIError(400) 'Tool call `function` must be a dictionary' is raised during OCI request construction.
Source
Thrown at litellm/llms/oci/chat/generic.py:129
)
def adapt_messages_to_generic_oci_standard_tool_call(role: str, tool_calls: list) -> OCIMessage:
"""Convert an assistant tool-call message to OCI format."""
tool_calls_formatted: Final = []
for tool_call in tool_calls:
if not isinstance(tool_call, dict):
raise OCIError(status_code=400, message="Each tool call must be a dictionary")
if tool_call.get("type") != "function":
raise OCIError(status_code=400, message="OCI only supports function tool calls")
tool_call_id = tool_call.get("id")
if not isinstance(tool_call_id, str):
raise OCIError(status_code=400, message="Tool call `id` must be a string")
tool_function = tool_call.get("function")
if not isinstance(tool_function, dict):
raise OCIError(status_code=400, message="Tool call `function` must be a dictionary")
function_name = tool_function.get("name")
if not isinstance(function_name, str):
raise OCIError(status_code=400, message="Tool call `function.name` must be a string")
arguments = tool_call["function"].get("arguments", "{}")
if not isinstance(arguments, str):
raise OCIError(
status_code=400,
message="Tool call `function.arguments` must be a JSON string",
)
tool_calls_formatted.append(
OCIToolCall(
id=tool_call_id,
type="FUNCTION",
name=function_name,
arguments=arguments,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Nest the descriptor: 'function': {'name': str, 'arguments': json_string}.
- When converting from other formats, wrap flat name/arguments into the nested object before sending.
- Validate structure with a pre-flight recursive check (see defense).
Example fix
# before
{'id':'c1','type':'function','name':'get_weather','arguments':'{}'}
# after
{'id':'c1','type':'function','function':{'name':'get_weather','arguments':'{}'}} Defensive patterns
Strategy: type-guard
Validate before calling
for tc in msg.get('tool_calls') or []:
assert isinstance(tc.get('function'), dict), f"tool call 'function' must be a dict: {tc!r}" Type guard
def tool_call_has_function_dict(tc: object) -> bool:
return isinstance(tc, dict) and isinstance(tc.get('function'), dict) Prevention
- Use the exact OpenAI nested layout {'function': {'name', 'arguments'}} everywhere; never flatten.
- Write a message-schema unit test that validates the history your app produces.
When it happens
Trigger: Sending tool calls like {'id':'c1','type':'function','function':'get_weather'} or omitting 'function' entirely when calling an oci/ GENERIC model with assistant tool-call history.
Common situations: Flattening the function object into sibling keys ({'name':..., 'arguments':...}) by mistake; JSON payloads built from tuples; serializers that drop nested objects on missing data.
Related errors
- Message `tool_calls` must be a list
- Each tool call must be a dictionary
- OCI only supports function tool calls
- Tool call `id` must be a string
- Tool call `function.name` must be a string
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/34bbc070b088fe9c.
Report an issue: GitHub.