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

  1. Ensure the model emits flat objects with a top-level 'name' field in fallback mode
  2. Configure the correct --tool-call-parser for the model so native parsing handles its format
  3. Enable/keep required tool_choice with a parser that guarantees the shape, or pre-normalize nested {"function":...} shapes
  4. 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

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


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/40f902f7e470706a. Report an issue: GitHub.