sgl-project/sglang · error · ValueError
every tool call must be a JSON object with a 'name'
Error message
every tool call must be a JSON object with a 'name'
What it means
The JSON-fallback tool-call parser decoded a JSON array but at least one element is not an object containing a 'name' key. Each element of the expected array must be a dict like {"name": ..., "parameters": {...}}. Raised in _process_tool_calls while building the chat response.
Source
Thrown at python/sglang/srt/entrypoints/openai/serving_chat.py:2176
# json_schema constraint → JSON array output for required/named
if is_required:
original_finish_type = finish_reason["type"]
if finish_reason["type"] == "stop":
finish_reason["type"] = "tool_calls"
finish_reason["matched"] = None
try:
tool_call_data = orjson.loads(text)
if isinstance(tool_call_data, dict):
tool_call_data = [tool_call_data]
if not isinstance(tool_call_data, list):
raise ValueError(
"expected a JSON array of tool calls, got "
f"{type(tool_call_data).__name__}"
)
if not all(
isinstance(tool, dict) and "name" in tool for tool in tool_call_data
):
raise ValueError(
"every tool call must be a JSON object with a 'name'"
)
tool_calls = []
for i, tool in enumerate(tool_call_data):
parameters = json.dumps(
tool.get("parameters", {}), ensure_ascii=False
)
call_info = ToolCallItem(
tool_index=i,
name=tool["name"],
parameters=parameters,
)
tool_id = self._process_tool_call_id(
call_info, history_tool_calls_cnt
)
tool_calls.append(
ToolCall(
id=tool_id,View on GitHub (pinned to 0132848349)
Solutions
- Ensure the model emits flat objects with a top-level 'name' field in fallback mode
- Configure the correct --tool-call-parser for the model so native parsing handles its format
- Enable/keep required tool_choice with a parser that guarantees the shape, or pre-normalize nested {"function":...} shapes
- Catch and degrade to plain content when tool JSON is malformed
Example fix
# before
[{"function": {"name": "get_weather", "arguments": "{}"}}]
# after
[{"name": "get_weather", "parameters": {}}] Defensive patterns
Strategy: type-guard
Validate before calling
assert all(isinstance(t, dict) and 'name' in t for t in tool_call_data)
Type guard
def is_valid_tool_list(v) -> bool:
return isinstance(v, list) and bool(v) and all(isinstance(t, dict) and isinstance(t.get('name'), str) for t in v) Try / catch
except ValueError as e: log and treat message.content as plain text
Prevention
- Normalize nested {"function": ...} shapes to flat before fallback parsing
- Prefer native tool parsers over JSON fallback
- Truncate-protect: ensure max_tokens large enough for full tool JSON
When it happens
Trigger: Model output parses to a list, but an element is a string/number or a dict missing "name" — e.g. ["get_weather"] or [{"function": {...}}] (OpenAI-style nested shape instead of flat).
Common situations: Model emitting OpenAI-format {"function": {"name": ...}} objects into the fallback path; models omitting name for forced/required tools; partially truncated JSON that still parses.
Related errors
- No call message found for {call_id}
- No tool calls but found tool output
- expected a JSON array of tool calls, got {type(tool_call_dat
- v_cache must be provided
- q can only be None when only_qv=True
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/40f902f7e470706a.
Report an issue: GitHub.