BerriAI/litellm · error · OCIError

Tool call `function.name` must be a string

Error message

Tool call `function.name` must be a string

What it means

Within a tool call's function dict, `name` must be a string because OCI GENERIC requires the invoked function's name. A missing, None, or non-string name raises OCIError 400 client-side. This is the last shape check before the OCIToolCall is constructed.

Solutions

  1. Always include a string function name matching one of the tools you declared
  2. Default/derive at build time: tc["function"].setdefault("name", fallback_name)
  3. Filter out malformed tool calls from replayed history rather than forwarding them
  4. Assert name is str in your history serializer

Example fix

# before
{"id":"1","type":"function","function":{"arguments":"{\"city\":\"SF\"}"}}

# after
{"id":"1","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"SF\"}"}}
Defensive patterns

Strategy: type-guard

Validate before calling

def drop_tool_calls_without_name(tool_calls: list) -> list:
    return [tc for tc in tool_calls if isinstance(tc.get("function", {}).get("name"), str)]

Type guard

def tool_call_has_string_name(tc) -> bool:
    fn = tc.get("function")
    return isinstance(fn, dict) and isinstance(fn.get("name"), str)

Prevention

When it happens

Trigger: tool_calls entries like {"function":{"arguments":"{}"}} (no name), {"function":{"name":None}}, or {"function":{"name":42}} replayed to an oci/generic.* model in assistant history.

Common situations: Models emitting tool calls without names when a name was optional in your prompt; history trimming dropping the name key; name populated from a variable that is occasionally undefined; renaming keys (func->name) mid-migration.

Related errors


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

Appendix: source

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

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

    return OCIMessage(

View on GitHub (pinned to 77b7c6c40c)