{"record":{"id":"40f902f7e470706a","repo":"sgl-project/sglang","slug":"every-tool-call-must-be-a-json-object-with-a-name","errorCode":null,"errorMessage":"every tool call must be a JSON object with a 'name'","messagePattern":"every tool call must be a JSON object with a 'name'","errorType":"exception","errorClass":"ValueError","httpStatus":500,"severity":"error","filePath":"python/sglang/srt/entrypoints/openai/serving_chat.py","lineNumber":2176,"sourceCode":"        # json_schema constraint → JSON array output for required/named\n        if is_required:\n            original_finish_type = finish_reason[\"type\"]\n            if finish_reason[\"type\"] == \"stop\":\n                finish_reason[\"type\"] = \"tool_calls\"\n                finish_reason[\"matched\"] = None\n            try:\n                tool_call_data = orjson.loads(text)\n                if isinstance(tool_call_data, dict):\n                    tool_call_data = [tool_call_data]\n                if not isinstance(tool_call_data, list):\n                    raise ValueError(\n                        \"expected a JSON array of tool calls, got \"\n                        f\"{type(tool_call_data).__name__}\"\n                    )\n                if not all(\n                    isinstance(tool, dict) and \"name\" in tool for tool in tool_call_data\n                ):\n                    raise ValueError(\n                        \"every tool call must be a JSON object with a 'name'\"\n                    )\n                tool_calls = []\n                for i, tool in enumerate(tool_call_data):\n                    parameters = json.dumps(\n                        tool.get(\"parameters\", {}), ensure_ascii=False\n                    )\n                    call_info = ToolCallItem(\n                        tool_index=i,\n                        name=tool[\"name\"],\n                        parameters=parameters,\n                    )\n                    tool_id = self._process_tool_call_id(\n                        call_info, history_tool_calls_cnt\n                    )\n                    tool_calls.append(\n                        ToolCall(\n                            id=tool_id,","sourceCodeStart":2158,"sourceCodeEnd":2194,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/openai/serving_chat.py#L2158-L2194","documentation":"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.","triggerScenarios":"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).","commonSituations":"Model emitting OpenAI-format {\"function\": {\"name\": ...}} objects into the fallback path; models omitting name for forced/required tools; partially truncated JSON that still parses.","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"],"exampleFix":"# before\n[{\"function\": {\"name\": \"get_weather\", \"arguments\": \"{}\"}}]\n# after\n[{\"name\": \"get_weather\", \"parameters\": {}}]","handlingStrategy":"type-guard","validationCode":"assert all(isinstance(t, dict) and 'name' in t for t in tool_call_data)","typeGuard":"def is_valid_tool_list(v) -> bool:\n    return isinstance(v, list) and bool(v) and all(isinstance(t, dict) and isinstance(t.get('name'), str) for t in v)","tryCatchPattern":"except ValueError as e: log and treat message.content as plain text","preventionTips":["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"],"tags":["tool-calls","json-parsing","validation"],"backgroundTag":"tool-call-json-parse-failed","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}