{"record":{"id":"4ba39c75f859f626","repo":"BerriAI/litellm","slug":"tool-call-not-supported-tool-call","errorCode":null,"errorMessage":"tool call not supported: {tool_call}","messagePattern":"tool call not supported: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/completion_extras/litellm_responses_transformation/transformation.py","lineNumber":346,"sourceCode":"                            \"type\": \"function_call\",\n                            \"call_id\": tool_call[\"id\"],\n                        }\n                        if \"name\" in function:\n                            input_tool_call[\"name\"] = function[\"name\"]\n                        if \"arguments\" in function:\n                            input_tool_call[\"arguments\"] = function[\"arguments\"]\n                        input_items.append(input_tool_call)\n                    elif isinstance(custom, dict):\n                        input_items.append(\n                            ResponseCustomToolCallParam(\n                                type=\"custom_tool_call\",\n                                call_id=tool_call[\"id\"],\n                                name=custom.get(\"name\", \"\"),\n                                input=custom.get(\"input\", \"\"),\n                            )\n                        )\n                    else:\n                        raise ValueError(f\"tool call not supported: {tool_call}\")\n            elif content is not None:\n                if role == \"assistant\":\n                    for r_item in _get_reasoning_items(msg):\n                        input_items.append(_reasoning_item_to_response_input(r_item))\n                input_items.append(\n                    {\n                        \"type\": \"message\",\n                        \"role\": role,\n                        \"content\": self._convert_content_to_responses_format(content, cast(str, role)),\n                    }\n                )\n\n        return input_items, instructions\n\n    def _map_optional_params_to_responses_api_request(\n        self,\n        optional_params: dict,\n        responses_api_request: \"ResponsesAPIOptionalRequestParams\",","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/completion_extras/litellm_responses_transformation/transformation.py#L328-L364","documentation":"Raised while transforming assistant tool_calls into Responses API input items: a tool call whose 'function' field is neither the standard OpenAI shape (with name/arguments) nor a dict-based custom tool call. The bridge only knows how to convert function tool calls and custom tool calls; anything else (e.g. provider-specific tool call formats) is rejected.","triggerScenarios":"Sending back an assistant message whose tool_calls entries come from a non-OpenAI provider (e.g. Anthropic-style tool_use blocks put directly into tool_calls), or a tool_call dict missing/typing 'function' as a non-dict, while the request is routed through the chat→responses bridge.","commonSituations":"Multi-turn agent loops that replay tool calls captured from a different provider; hand-crafted assistant tool_call dicts; converting between provider SDK formats without normalization.","solutions":["Normalize prior assistant tool_calls to the OpenAI shape before replaying: each entry needs id, type='function', function={'name': str, 'arguments': str}","If the tool call was a custom/freeform tool, ensure the 'function' field is a dict with 'name' and 'input'","Re-run the original tool-calling turn through the same provider so the replayed format matches","Bypass the responses bridge for that call if you must preserve a foreign tool call format"],"exampleFix":"# before\nmessages = [\n  {\"role\": \"assistant\", \"tool_calls\": [\n      {\"id\": \"t1\", \"type\": \"tool_use\", \"input\": {\"city\": \"SF\"}}  # non-OpenAI shape\n  ]},\n]\n\n# after\nimport json\nmessages = [\n  {\"role\": \"assistant\", \"tool_calls\": [\n      {\"id\": \"t1\", \"type\": \"function\",\n       \"function\": {\"name\": \"get_weather\", \"arguments\": json.dumps({\"city\": \"SF\"})}}\n  ]},\n]","handlingStrategy":"validation","validationCode":"def tool_calls_bridge_safe(tool_calls: list) -> bool:\n    for tc in tool_calls:\n        fn = tc.get(\"function\") if isinstance(tc, dict) else None\n        if not (isinstance(fn, dict) and \"arguments\" in fn):\n            return False\n    return True","typeGuard":"def is_openai_tool_call(tc) -> bool:\n    return (\n        isinstance(tc, dict)\n        and isinstance(tc.get(\"function\"), dict)\n        and isinstance(tc[\"function\"].get(\"arguments\"), str)\n    )","tryCatchPattern":"try:\n    resp = litellm.completion(**bridge_kwargs)\nexcept ValueError as e:\n    if \"tool call not supported\" in str(e):\n        # drop history tool_calls and retry with a text summary of the tool result\n        bridge_kwargs[\"messages\"] = sanitize_tool_calls(bridge_kwargs[\"messages\"])\n        resp = litellm.completion(**bridge_kwargs)\n    else:\n        raise","preventionTips":["Normalize tool calls to OpenAI format whenever you cross providers","Serialize arguments as a JSON string in function.arguments","Keep one shared converter for replaying tool results in agent loops"],"tags":["tool-calls","transformation","responses-api","bridge"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}