{"record":{"id":"34bbc070b088fe9c","repo":"BerriAI/litellm","slug":"tool-call-function-must-be-a-dictionary","errorCode":null,"errorMessage":"Tool call `function` must be a dictionary","messagePattern":"Tool call `function` must be a dictionary","errorType":"exception","errorClass":"OCIError","httpStatus":400,"severity":"error","filePath":"litellm/llms/oci/chat/generic.py","lineNumber":129,"sourceCode":"    )\n\n\ndef adapt_messages_to_generic_oci_standard_tool_call(role: str, tool_calls: list) -> OCIMessage:\n    \"\"\"Convert an assistant tool-call message to OCI format.\"\"\"\n    tool_calls_formatted: Final = []\n    for tool_call in tool_calls:\n        if not isinstance(tool_call, dict):\n            raise OCIError(status_code=400, message=\"Each tool call must be a dictionary\")\n        if tool_call.get(\"type\") != \"function\":\n            raise OCIError(status_code=400, message=\"OCI only supports function tool calls\")\n\n        tool_call_id = tool_call.get(\"id\")\n        if not isinstance(tool_call_id, str):\n            raise OCIError(status_code=400, message=\"Tool call `id` must be a string\")\n\n        tool_function = tool_call.get(\"function\")\n        if not isinstance(tool_function, dict):\n            raise OCIError(status_code=400, message=\"Tool call `function` must be a dictionary\")\n\n        function_name = tool_function.get(\"name\")\n        if not isinstance(function_name, str):\n            raise OCIError(status_code=400, message=\"Tool call `function.name` must be a string\")\n\n        arguments = tool_call[\"function\"].get(\"arguments\", \"{}\")\n        if not isinstance(arguments, str):\n            raise OCIError(\n                status_code=400,\n                message=\"Tool call `function.arguments` must be a JSON string\",\n            )\n\n        tool_calls_formatted.append(\n            OCIToolCall(\n                id=tool_call_id,\n                type=\"FUNCTION\",\n                name=function_name,\n                arguments=arguments,","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/oci/chat/generic.py#L111-L147","documentation":"Inside each function tool call, the `function` field must be a dict containing name and arguments. If it is missing, None, a string (double-encoded JSON), or any non-dict, OCI translation aborts with OCIError 400. Mirrors the OpenAI tool-call structure that OCI GENERIC maps onto OCIToolCall.","triggerScenarios":"Assistant history with tool_calls=[{\"id\":\"1\",\"type\":\"function\",\"function\":\"{\\\"name\\\":\\\"f\\\"}\"}] (function JSON-stringified) or with the function key omitted, sent to an oci/generic.* model.","commonSituations":"json.dumps applied one level too deep when persisting history; hand-crafted tool_calls from an LLM's raw text output parsed shallowly; frameworks storing function as a flat string for compactness; partial dict after aggressive key filtering.","solutions":["Keep `function` a dict: {\"name\": ..., \"arguments\": \"<json str>\"}","Repair double-encoded history at load: json.loads(tc[\"function\"]) when it is a str","Echo the structure exactly as returned by prior completion responses","Validate history shape (see type guard) before the first OCI call"],"exampleFix":"# before\n{\"id\":\"1\",\"type\":\"function\",\"function\":\"{\\\"name\\\":\\\"get_weather\\\",\\\"arguments\\\":\\\"{}\\\"}\"}\n\n# after\n{\"id\":\"1\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{}\"}}","handlingStrategy":"validation","validationCode":"import json\n\ndef normalize_tool_call_function(tool_calls: list) -> list:\n    out = []\n    for tc in tool_calls:\n        fn = tc.get(\"function\")\n        if isinstance(fn, str):\n            tc = {**tc, \"function\": json.loads(fn)}\n        out.append(tc)\n    return out","typeGuard":"def tool_call_function_is_dict(tc) -> bool:\n    return isinstance(tc.get(\"function\"), dict)","tryCatchPattern":null,"preventionTips":["Round-trip test your history store: save then load and assert the shape is unchanged","Validate history at load time, not just at send time"],"tags":["oci","tool-calls","request-validation","serialization"],"backgroundTag":"request-body-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}