BerriAI/litellm · error · OCIError

OCI only supports function tool calls

Error message

OCI only supports function tool calls

What it means

OCI GENERIC only implements function tool calls, so each tool_call object in an assistant message must have type == "function". Any other type (or missing type, which compares != "function") raises OCIError 400 in adapt_messages_to_generic_oci_standard_tool_call. The whitelist mirrors OCI's FUNCTION-only tool-call surface.

Solutions

  1. Ensure every tool_call has "type": "function" alongside id and function fields
  2. Strip non-function tool calls from history before sending to OCI (they can't be represented)
  3. When rebuilding trimmed histories, preserve all keys or re-serialize from the original response object
  4. Log offending tool_call objects at build time to catch dropped keys early

Example fix

# before
{"role": "assistant", "tool_calls": [{"id": "1", "function": {"name": "f"}}]}  # type missing

# after
{"role": "assistant", "tool_calls": [{"id": "1", "type": "function", "function": {"name": "f", "arguments": "{}"}}]}
Defensive patterns

Strategy: validation

Validate before calling

def normalize_tool_call_types(tool_calls: list) -> list:
    return [tc for tc in tool_calls if isinstance(tc, dict) and tc.get("type") == "function"]

Type guard

def is_function_tool_call(tc) -> bool:
    return isinstance(tc, dict) and tc.get("type") == "function"

Prevention

When it happens

Trigger: Assistant history containing {"type":"code_interpreter",...} or tool-call objects missing the type key, sent to an oci/generic.* model. Note that a missing `type` also triggers this (None != "function"), so partially-built objects fail here too.

Common situations: Porting agent histories from OpenAI with code_interpreter/retrieval/function variants; trimming tool_call dicts (dropping type) when compressing context; merging tool schemas from multiple providers; custom tool-call types invented by frameworks.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/a0c2048c40ad593b. Report an issue: GitHub.

Appendix: source

Thrown at litellm/llms/oci/chat/generic.py:121

                )
            new_content.append(OCIImageContentPart(imageUrl=OCIImageUrl(url=image_url)))

    return OCIMessage(
        role=open_ai_to_generic_oci_role_map[role],
        content=new_content,
        toolCalls=None,
        toolCallId=None,
    )


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",

View on GitHub (pinned to 77b7c6c40c)