BerriAI/litellm · error · ValueError

Unsupported type {type(value)} for key function_call in mess

Error message

Unsupported type {type(value)} for key function_call in message {message}

What it means

For the legacy OpenAI 'function_call' field, the counter expects a mapping like {"name":..., "arguments":...}. A non-mapping value (string, list, None) cannot be read with .get('arguments'), so it raises, echoing the value's type and the message.

Source

Thrown at litellm/litellm_core_utils/token_counter.py:429

    Handles both the modern `tool_calls` list and the legacy OpenAI
    `function_call` dict. Only the `arguments` string is counted (matching the
    existing tool_calls behavior); names are accounted for elsewhere via the
    tool/function definitions and `tool_choice`.
    """
    if key == "tool_calls":
        if not isinstance(value, list):
            raise ValueError(f"Unsupported type {type(value)} for key tool_calls in message {message}")
        total = 0
        for tool_call in value:
            if "function" not in tool_call:
                raise ValueError(f"Unsupported tool call {tool_call} must contain a function key")
            function_arguments = tool_call["function"].get("arguments", "")
            total += count_function(str(function_arguments))
        return total
    if key == "function_call":
        if not isinstance(value, Mapping):
            raise ValueError(f"Unsupported type {type(value)} for key function_call in message {message}")
        return count_function(str(value.get("arguments", "")))
    raise ValueError(f"Unexpected key {key!r}; expected 'tool_calls' or 'function_call'")


def _count_messages(
    params: _MessageCountParams,
    messages: list[AllMessageValues],
    use_default_image_token_count: bool,
    default_token_count: int | None,
) -> int:
    """
    Count the number of tokens in a list of messages.

    Args:
        params (_MessageCountParams): The parameters for counting tokens.
        messages (List[AllMessageValues]): The list of messages to count tokens in.
        use_default_image_token_count (bool): When True, will NOT make a GET request to the image URL and instead return the default image dimensions.
        default_token_count (Optional[int]): The default number of tokens to return for a message block, if an error occurs.

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Store function_call as a dict: {"name": <str>, "arguments": <json-string>} (arguments stays a string per the OpenAI spec).
  2. Drop null/empty function_call fields from persisted assistant messages.

Example fix

# before
msg = {"role": "assistant", "function_call": "get_weather({city: 'SF'})"}

# after
msg = {"role": "assistant",
       "function_call": {"name": "get_weather", "arguments": "{\"city\": \"SF\"}"}}
Defensive patterns

Strategy: type-guard

Validate before calling

from collections.abc import Mapping
fc = msg.get("function_call")
if fc is not None and not isinstance(fc, Mapping):
    raise ValueError("function_call must be a dict with name/arguments")
n = litellm.token_counter(model=m, messages=msgs)

Type guard

from collections.abc import Mapping

def is_valid_function_call(value) -> bool:
    return isinstance(value, Mapping) and isinstance(value.get("arguments", ""), str)

Prevention

When it happens

Trigger: Assistant messages with function_call stored as a plain string (e.g. "get_weather(...)") instead of a dict, or function_call set to a list or null on legacy function-calling traffic passed to token_counter or cost accounting.

Common situations: Migrating old OpenAI function-calling logs into LiteLLM accounting; agent frameworks that stringify function calls for logging and reuse the object for counting; schema drift between stored and expected shapes.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/897e741d90839c85. Report an issue: GitHub.