{"record":{"id":"ef44c6c7ae4220dd","repo":"sgl-project/sglang","slug":"deepseek-v4-merges-tool-messages-into-user-please","errorCode":null,"errorMessage":"deepseek_v4 merges tool messages into user; please preprocess with merge_tool_messages()","messagePattern":"deepseek_v4 merges tool messages into user; please preprocess with merge_tool_messages\\(\\)","errorType":"validation","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/entrypoints/openai/encoding_dsv4.py","lineNumber":388,"sourceCode":"                            if b.get(\"type\") == \"text\":\n                                text_parts.append(b.get(\"text\", \"\"))\n                            else:\n                                text_parts.append(f\"[Unsupported {b.get('type')}]\")\n                        tool_content = \"\\n\\n\".join(text_parts)\n                    parts.append(tool_output_template.format(content=tool_content))\n                else:\n                    parts.append(f\"[Unsupported {block_type}]\")\n            prompt += \"\\n\\n\".join(parts)\n        else:\n            prompt += content or \"\"\n\n    elif role == \"latest_reminder\":\n        prompt += LATEST_REMINDER_SP_TOKEN + latest_reminder_msg_template.format(\n            content=content\n        )\n\n    elif role == \"tool\":\n        raise NotImplementedError(\n            \"deepseek_v4 merges tool messages into user; please preprocess with merge_tool_messages()\"\n        )\n\n    elif role == \"assistant\":\n        thinking_part = \"\"\n        tc_content = \"\"\n\n        if tool_calls:\n            tc_list = [\n                tool_call_template.format(\n                    dsml_token=dsml_token,\n                    name=tc.get(\"name\"),\n                    arguments=encode_arguments_to_dsml(tc),\n                )\n                for tc in tool_calls\n            ]\n            tc_content += \"\\n\\n\" + tool_calls_template.format(\n                dsml_token=dsml_token,","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/openai/encoding_dsv4.py#L370-L406","documentation":"deepseek_v4's template merges tool outputs into the following user message; raw 'tool' role messages are not rendered and raise NotImplementedError, telling you to preprocess with merge_tool_messages().","triggerScenarios":"Sending a messages array containing role='tool' directly to the DSv4 encoder/serving endpoint without calling merge_tool_messages() first.","commonSituations":"Replaying OpenAI-style tool transcripts verbatim; client frameworks that always emit tool-role messages; missing preprocessing step in a custom integration.","solutions":["Call merge_tool_messages(messages) before encoding/sending.","Or configure the serving adapter to apply the merge automatically.","Replace tool messages with merged user messages containing <function_results> blocks if merging manually."],"exampleFix":"# before\nmessages = [{...assistant w/ tool_calls...},{\"role\":\"tool\",\"content\":\"42\"}]\n# after\nfrom sglang.srt.entrypoints.openai.encoding_dsv4 import merge_tool_messages\nmessages = merge_tool_messages(messages)","handlingStrategy":"fallback","validationCode":"from sglang.srt.entrypoints.openai.encoding_dsv4 import merge_tool_messages\nmessages=[m for m in messages if m['role']!='tool'] or merge_tool_messages(messages)","typeGuard":"def has_raw_tool_role(msgs): return any(m['role']=='tool' for m in msgs)","tryCatchPattern":null,"preventionTips":["Run merge_tool_messages() on every OpenAI-style transcript before DSv4 encoding."],"tags":["deepseek-v4","tool-calls","preprocessing"],"backgroundTag":"tool-role-not-supported","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}