{"record":{"id":"897e741d90839c85","repo":"BerriAI/litellm","slug":"unsupported-type-type-value-for-key-function-ca","errorCode":null,"errorMessage":"Unsupported type {type(value)} for key function_call in message {message}","messagePattern":"Unsupported type (.+?) for key function_call in message (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/token_counter.py","lineNumber":429,"sourceCode":"\n    Handles both the modern `tool_calls` list and the legacy OpenAI\n    `function_call` dict. Only the `arguments` string is counted (matching the\n    existing tool_calls behavior); names are accounted for elsewhere via the\n    tool/function definitions and `tool_choice`.\n    \"\"\"\n    if key == \"tool_calls\":\n        if not isinstance(value, list):\n            raise ValueError(f\"Unsupported type {type(value)} for key tool_calls in message {message}\")\n        total = 0\n        for tool_call in value:\n            if \"function\" not in tool_call:\n                raise ValueError(f\"Unsupported tool call {tool_call} must contain a function key\")\n            function_arguments = tool_call[\"function\"].get(\"arguments\", \"\")\n            total += count_function(str(function_arguments))\n        return total\n    if key == \"function_call\":\n        if not isinstance(value, Mapping):\n            raise ValueError(f\"Unsupported type {type(value)} for key function_call in message {message}\")\n        return count_function(str(value.get(\"arguments\", \"\")))\n    raise ValueError(f\"Unexpected key {key!r}; expected 'tool_calls' or 'function_call'\")\n\n\ndef _count_messages(\n    params: _MessageCountParams,\n    messages: list[AllMessageValues],\n    use_default_image_token_count: bool,\n    default_token_count: int | None,\n) -> int:\n    \"\"\"\n    Count the number of tokens in a list of messages.\n\n    Args:\n        params (_MessageCountParams): The parameters for counting tokens.\n        messages (List[AllMessageValues]): The list of messages to count tokens in.\n        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.\n        default_token_count (Optional[int]): The default number of tokens to return for a message block, if an error occurs.","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/token_counter.py#L411-L447","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Store function_call as a dict: {\"name\": <str>, \"arguments\": <json-string>} (arguments stays a string per the OpenAI spec).","Drop null/empty function_call fields from persisted assistant messages."],"exampleFix":"# before\nmsg = {\"role\": \"assistant\", \"function_call\": \"get_weather({city: 'SF'})\"}\n\n# after\nmsg = {\"role\": \"assistant\",\n       \"function_call\": {\"name\": \"get_weather\", \"arguments\": \"{\\\"city\\\": \\\"SF\\\"}\"}}","handlingStrategy":"type-guard","validationCode":"from collections.abc import Mapping\nfc = msg.get(\"function_call\")\nif fc is not None and not isinstance(fc, Mapping):\n    raise ValueError(\"function_call must be a dict with name/arguments\")\nn = litellm.token_counter(model=m, messages=msgs)","typeGuard":"from collections.abc import Mapping\n\ndef is_valid_function_call(value) -> bool:\n    return isinstance(value, Mapping) and isinstance(value.get(\"arguments\", \"\"), str)","tryCatchPattern":null,"preventionTips":["Keep legacy function_call as {name, arguments-json-string}; never store it stringified.","Drop null function_call keys when persisting assistant messages."],"tags":["token-counter","function-call","validation","legacy"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}