{"record":{"id":"10a426908e105140","repo":"sgl-project/sglang","slug":"role-must-be-one-of-allowed-case-insensitive","errorCode":null,"errorMessage":"'role' must be one of {allowed} (case-insensitive).","messagePattern":"'role' must be one of (.+?) \\(case-insensitive\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/entrypoints/openai/protocol.py","lineNumber":707,"sourceCode":"class ChatCompletionMessageGenericParam(BaseModel):\n    role: _GenericMessageRole\n    content: Union[str, List[ChatCompletionMessageContentPart], None] = Field(\n        default=None\n    )\n    tool_call_id: Optional[str] = None\n    name: Optional[str] = None\n    reasoning_content: Optional[str] = None\n    tool_calls: Optional[List[ToolCall]] = Field(default=None, examples=[None])\n    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","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/openai/protocol.py#L689-L725","documentation":"Message role normalization: role must be one of the generic message roles (case-insensitive); unknown strings are rejected with the allowed list.","triggerScenarios":"role='tool_call', 'bot', 'Tool' (if 'tool' allowed but case rule fails on some path), or any string not in _GENERIC_MESSAGE_ROLES.","commonSituations":"Custom agent frameworks with invented roles; typos; roles from other provider APIs.","solutions":["Use one of the roles printed in the error (e.g. system/user/assistant/tool/developer), lowercase.","Map custom roles to the nearest standard role client-side before sending."],"exampleFix":"# before\n{\"role\":\"bot\",\"content\":\"hi\"}\n# after\n{\"role\":\"assistant\",\"content\":\"hi\"}","handlingStrategy":"type-guard","validationCode":"ROLES={'system','user','assistant','tool','developer'}\nfor m in messages:\n    assert isinstance(m['role'],str) and m['role'].lower() in ROLES","typeGuard":"def is_valid_role(r): return isinstance(r,str) and r.lower() in {'system','user','assistant','tool','developer'}","tryCatchPattern":null,"preventionTips":["Lowercase and validate roles before requests."],"tags":["openai-api","role","validation"],"backgroundTag":"unknown-message-role","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}