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

  1. Set exactly one field: {'type':'thinking','thinking':'...'} or {'type':'thinking','text':'...'}.
  2. 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

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


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)