sgl-project/sglang · error · ValueError
thinking parts require exactly one of 'thinking' or 'text'
Error message
thinking parts require exactly one of 'thinking' or 'text'
What it means
ChatCompletionMessageContentThinkingPart must contain exactly one of 'thinking' or 'text'. The model validator rejects when both are set or both are None/missing.
Source
Thrown at python/sglang/srt/entrypoints/openai/protocol.py:544
if self.sglext is None:
data.pop("sglext", None)
return data
class ChatCompletionMessageContentTextPart(BaseModel):
type: Literal["text"]
text: str
class ChatCompletionMessageContentThinkingPart(BaseModel):
type: Literal["thinking", "reasoning"]
thinking: Optional[str] = None
text: Optional[str] = None
@model_validator(mode="after")
def validate_payload(self):
if (self.thinking is None) == (self.text is None):
raise ValueError(
"thinking parts require exactly one of 'thinking' or 'text'"
)
return self
class ChatCompletionMessageContentImageURL(BaseModel):
url: str
detail: Optional[Literal["auto", "low", "high"]] = "auto"
max_dynamic_patch: Optional[int] = None
min_dynamic_patch: Optional[int] = None
content_hash: Optional[str] = None
@field_validator("content_hash")
@classmethod
def validate_content_hash(cls, value: Optional[str]) -> Optional[str]:
from sglang.srt.multimodal.cache import parse_content_hash
return parse_content_hash(value)View on GitHub (pinned to 0132848349)
Solutions
- Set exactly one field: {'type':'thinking','thinking':'...'} or {'type':'thinking','text':'...'}.
- Audit serializers that null out empty strings before sending.
Example fix
# before
{"type":"thinking"}
# after
{"type":"thinking","thinking":"the model reasons here"} Defensive patterns
Strategy: type-guard
Validate before calling
part={'type':'thinking'}
part['thinking']=txt # set exactly one key
assert (part.get('thinking') is None) != (part.get('text') is None) Type guard
def valid_thinking_part(p):
return (p.get('thinking') is None) != (p.get('text') is None) Prevention
- Construct thinking parts with exactly one payload field.
When it happens
Trigger: Content part {'type':'thinking'} with neither thinking nor text, or a part containing both keys.
Common situations: Constructing thinking parts programmatically with defaults; serialization dropping empty fields so both become None; copying reasoning into both fields.
Related errors
- max_tokens must be positive
- thinking content parts are only valid in assistant messages
- Invalid request body: {e}
- 'role' must be one of {allowed} (case-insensitive).
- 'role' must be a string
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/ae76f2863a15a41c.
Report an issue: GitHub.