BerriAI/litellm · error · OCIError
Tool call `id` must be a string
Error message
Tool call `id` must be a string
What it means
Each tool call in an assistant message must carry a string 'id' so LiteLLM can map it into OCIToolCall(id=...). A missing, None, or non-string id raises OCIError(400) 'Tool call `id` must be a string' client-side. The id is what later role='tool' messages reference via tool_call_id, so it cannot be omitted.
Source
Thrown at litellm/llms/oci/chat/generic.py:125
role=open_ai_to_generic_oci_role_map[role],
content=new_content,
toolCalls=None,
toolCallId=None,
)
def adapt_messages_to_generic_oci_standard_tool_call(role: str, tool_calls: list) -> OCIMessage:
"""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(View on GitHub (pinned to 6c2dcb801b)
Solutions
- Give every tool call a unique string id and reuse it in the matching tool result's tool_call_id (e.g. 'call_001').
- If generating synthetic history, mint ids with uuid4().hex or a counter.
- Pre-flight check: all(isinstance(tc.get('id'), str) and tc['id'] for tc in msg['tool_calls']).
Example fix
# before
{'type':'function','function':{'name':'f','arguments':'{}'}}
# after
{'id':'call_001','type':'function','function':{'name':'f','arguments':'{}'}}
# and the tool reply: {'role':'tool','tool_call_id':'call_001','content':'42'} Defensive patterns
Strategy: validation
Validate before calling
import uuid
for tc in msg.get('tool_calls') or []:
if not isinstance(tc.get('id'), str) or not tc['id']:
tc['id'] = f'call_{uuid.uuid4().hex[:8]}' Type guard
def tool_call_has_string_id(tc: object) -> bool:
return isinstance(tc, dict) and isinstance(tc.get('id'), str) and bool(tc['id']) Prevention
- Always pair: each tool_call id gets exactly one matching tool_call_id in the tool reply.
- When fabricating synthetic history, generate ids from a counter or uuid and keep them stable in the transcript.
When it happens
Trigger: Hand-constructing assistant tool call history without an 'id' (e.g. {'type':'function','function':{...}}), or with id set to an integer/None, in a request to an oci/ GENERIC model.
Common situations: Fabricating synthetic assistant turns for few-shot tool use and forgetting the id; a database schema storing tool calls without ids; trimming ids 'to save tokens' in long conversations.
Related errors
- Tool call `function.name` must be a string
- Tool result message must have a string `tool_call_id`
- Each tool call must be a dictionary
- OCI only supports function tool calls
- Tool call `function` must be a dictionary
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/55dad91d9fb2663b.
Report an issue: GitHub.