{"record":{"id":"60c432d93ea00f9a","repo":"sgl-project/sglang","slug":"thinking-content-parts-are-only-valid-in-assistant","errorCode":null,"errorMessage":"thinking content parts are only valid in assistant messages","messagePattern":"thinking content parts are only valid in assistant messages","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/entrypoints/openai/protocol.py","lineNumber":716,"sourceCode":"    tools: Optional[List[Tool]] = Field(default=None, examples=[None])\n\n    @field_validator(\"role\", mode=\"before\")\n    @classmethod\n    def _normalize_role(cls, v):\n        if isinstance(v, str):\n            v_lower = v.lower()\n            if v_lower not in _GENERIC_MESSAGE_ROLES:\n                allowed = \", \".join(repr(r) for r in _GENERIC_MESSAGE_ROLES)\n                raise ValueError(f\"'role' must be one of {allowed} (case-insensitive).\")\n            return v_lower\n        raise ValueError(\"'role' must be a string\")\n\n    @model_validator(mode=\"after\")\n    def validate_thinking_parts_role(self):\n        if self.role != \"assistant\" and isinstance(self.content, list):\n            for part in self.content:\n                if isinstance(part, ChatCompletionMessageContentThinkingPart):\n                    raise ValueError(\n                        \"thinking content parts are only valid in assistant messages\"\n                    )\n        return self\n\n\nclass ChatCompletionMessageUserParam(BaseModel):\n    role: Literal[\"user\"]\n    content: Union[str, List[ChatCompletionMessageContentPart]]\n\n    @model_validator(mode=\"after\")\n    def validate_thinking_parts_role(self):\n        if isinstance(self.content, list):\n            for part in self.content:\n                if isinstance(part, ChatCompletionMessageContentThinkingPart):\n                    raise ValueError(\n                        \"thinking content parts are only valid in assistant messages\"\n                    )\n        return self","sourceCodeStart":698,"sourceCodeEnd":734,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/openai/protocol.py#L698-L734","documentation":"ChatCompletionMessageParam (assistant) rejects content arrays containing a thinking part: thinking content parts are only valid in assistant messages — this validator fires on the non-assistant param class that still received one.","triggerScenarios":"A non-assistant message (user/system/developer/tool) whose content is a list containing {'type':'thinking',...}.","commonSituations":"Echoing the assistant's structured thinking back into a user message; naive message copying in agent loops.","solutions":["Remove thinking parts from non-assistant messages; keep them only in assistant role.","Convert thinking content to plain text if you must reference it in a user message."],"exampleFix":"# before\n{\"role\":\"user\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"...\"},{\"type\":\"text\",\"text\":\"q\"}]}\n# after\n{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"q\"}]}","handlingStrategy":"validation","validationCode":"for m in messages:\n    if m['role']!='assistant' and isinstance(m.get('content'),list):\n        assert all(p.get('type')!='thinking' for p in m['content'])","typeGuard":"def no_thinking_in_non_assistant(m):\n    return m['role']=='assistant' or not isinstance(m.get('content'),list) or all(p.get('type')!='thinking' for p in m['content'])","tryCatchPattern":null,"preventionTips":["Keep thinking parts exclusively in assistant messages."],"tags":["openai-api","thinking-part","role-validation"],"backgroundTag":"thinking-content-misplaced","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}