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
OpenAI API request validation error: a 'thinking'-type content part must set exactly one of the 'thinking' or 'text' fields, not both and not neither.
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
- Assistant tool call function.arguments must be a JSON…
- Assistant tool call function.arguments must be valid JSON.
- 'data_parallel_rank' is deprecated, use 'routed_dp_rank'…
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/ae76f2863a15a41c.
Report an issue: GitHub.
Appendix: 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)