hiyouga/LlamaFactory · error · ValueError
tool_call must be a JSON object with 'name' and 'arguments'
Error message
tool_call must be a JSON object with 'name' and 'arguments' keys, got {tc!r} What it means
In the multimodal branch of _to_hf_messages, the tool_call value parsed as JSON but the result is not a dict containing both 'name' and 'arguments' keys. The renderer needs those two fields to build the HF {'type': 'function', 'function': {...}} structure, so anything else (a list, a string, a dict missing keys) is rejected.
Source
Thrown at src/llamafactory/v1/core/rendering/format.py:58
hf_messages = []
for message in messages:
tool_calls: list[dict] = []
reasoning_content = ""
if is_multimodal:
hf_content = []
for content in message["content"]:
if content["type"] == "text":
hf_content.append({"type": "text", "text": content["value"]})
elif content["type"] == "reasoning":
reasoning_content += content["value"]
elif content["type"] == "tool_call":
try:
tc = json.loads(content["value"])
except json.JSONDecodeError as e:
raise ValueError(f"tool_call value is not valid JSON: {content['value']!r}") from e
if not isinstance(tc, dict) or "name" not in tc or "arguments" not in tc:
raise ValueError(
f"tool_call must be a JSON object with 'name' and 'arguments' keys, got {tc!r}"
)
tool_calls.append(
{"type": "function", "function": {"name": tc["name"], "arguments": tc["arguments"]}}
)
elif content["type"] == "image_url":
hf_content.append({"type": "image", "image": content["value"]})
elif content["type"] == "video_url":
hf_content.append({"type": "video", "video": content["value"]})
elif content["type"] == "audio_url":
hf_content.append({"type": "audio", "audio": content["value"]})
hf_msg = {"role": message["role"], "content": hf_content}
else:
text = ""
for content in message["content"]:
if content["type"] == "text":
text += content["value"]
elif content["type"] == "reasoning":View on GitHub (pinned to f28afaf635)
Solutions
- Normalize each tool_call to a flat JSON object with exactly accessible 'name' and 'arguments' keys
- Unwrap OpenAI-style nesting: tc = json.loads(v); tc = tc['function'] if 'function' in tc else tc, then re-serialize
- Unwrap array-wrapped entries: take the first element if json.loads(v) returns a list of one call
Example fix
# before (OpenAI request shape)
value = json.dumps({"type": "function", "function": {"name": "f", "arguments": "{}"}})
# after (flat shape this renderer expects)
value = json.dumps({"name": "f", "arguments": {}}) Defensive patterns
Strategy: validation
Validate before calling
import json
def is_flat_tool_call(value: str) -> bool:
try:
tc = json.loads(value)
except json.JSONDecodeError:
return False
return isinstance(tc, dict) and "name" in tc and "arguments" in tc Prevention
- Standardize on the flat {'name', 'arguments'} shape in converters
- Unwrap OpenAI 'function'-nested objects before serializing
When it happens
Trigger: A multimodal message with a tool_call block whose value parses to e.g. "[{\"name\": ...}]" (array-wrapped), a bare string, or an object like {"function": {...}} (OpenAI request shape) instead of the expected flat {'name', 'arguments'} object.
Common situations: Feeding raw OpenAI-style payloads where the object is nested under 'function'; dataset normalization that wraps single objects in arrays; hand-written sample fixtures.
Related errors
- tool_call value is not valid JSON: {content['value']!r}
- Invalid JSON format in function message: {str([content])}.
- {kind} placeholder count ({seen}) != number of {kind} blocks
- tools is not valid JSON: {tools!r}
- Unsupported dummy media modality: {modality!r} (expected ima
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/0314258c2ff7ee05.
Report an issue: GitHub.