{"record":{"id":"fe1f9d95ee66ddba","repo":"sgl-project/sglang","slug":"inkling-thinking-parts-require-role-assistant","errorCode":null,"errorMessage":"Inkling thinking parts require role='assistant'","messagePattern":"Inkling thinking parts require role='assistant'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/parser/inkling_renderer.py","lineNumber":113,"sourceCode":"        )\n\n    for message_index, message in enumerate(message_list):\n        if message_index == leading_system_count:\n            append_effort()\n        role = _expect_role(message)\n        if role == \"tool\":\n            tool_name = str(\n                message.get(\"name\")\n                or tool_call_id_to_name.get(message.get(\"tool_call_id\") or \"\", \"\")\n            )\n            # The MM processor harvests media from tool messages, so coercing content\n            # to one string would drop images and desync the placeholder count.\n            tool_parts = list(_iter_render_parts(message.get(\"content\", \"\")))\n            if not tool_parts:\n                tool_parts = [(\"text\", \"\")]  # else the answered tool_call dangles\n            for kind, text in tool_parts:\n                if kind == \"thinking\":\n                    raise ValueError(\"Inkling thinking parts require role='assistant'\")\n                _append_message(\n                    input_ids,\n                    tokenizer,\n                    \"tool\",\n                    kind,\n                    text,\n                    author_name=tool_name,\n                )\n            continue\n\n        parts = list(_iter_render_parts(message.get(\"content\", \"\")))\n        turn_start = len(input_ids)\n        if role == \"assistant\":\n            reasoning_content = message.get(\"reasoning_content\")\n            if reasoning_content:\n                if not isinstance(reasoning_content, str):\n                    raise TypeError(\n                        \"assistant reasoning_content must be a string for Inkling rendering\"","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/parser/inkling_renderer.py#L95-L131","documentation":"The Inkling renderer encodes content parts in message order; 'thinking' parts are only meaningful for assistant messages. If a message with role 'tool' contains a thinking part, render_inkling_messages raises this ValueError before encoding.","triggerScenarios":"Sending a tool-role message whose content includes a part with \"type\":\"thinking\" or \"reasoning\" when using an Inkling-format model.","commonSituations":"Replaying recorded conversations that mixed reasoning parts into tool outputs; client code that attaches thinking parts to every message generically.","solutions":["Remove thinking/reasoning parts from tool and user messages","Move reasoning content into the assistant message's reasoning_content field"],"exampleFix":"# before\n{\"role\":\"tool\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"hmm\"},{\"type\":\"text\",\"text\":\"42\"}]}\n# after\n{\"role\":\"tool\",\"content\":[{\"type\":\"text\",\"text\":\"42\"}]}","handlingStrategy":"validation","validationCode":"for m in messages:\n    if m.get(\"role\") != \"assistant\":\n        assert not any(isinstance(p, dict) and p.get(\"type\") in (\"thinking\",\"reasoning\") for p in (m.get(\"content\") or []) if not isinstance(p, str))","typeGuard":"def no_thinking_in_non_assistant(m):\n    if m.get(\"role\") == \"assistant\": return True\n    c = m.get(\"content\", \"\")\n    return isinstance(c, str) or not any(isinstance(p, dict) and p.get(\"type\") in (\"thinking\",\"reasoning\") for p in c)","tryCatchPattern":null,"preventionTips":["Only put reasoning in assistant messages","Validate message shape before sending Inkling conversations"],"tags":["inkling","reasoning","content-parts","parser"],"backgroundTag":"invalid-message-structure","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}