sgl-project/sglang · error · DS32EncodingError

ThinkingMode: {thinking_mode}, invalid message without reaso

Error message

ThinkingMode: {thinking_mode}, invalid message without reasoning_content/tool_calls `{msg}` after last user message

What it means

In thinking mode, every message after the last user message must carry reasoning_content or tool_calls. The DSv32 encoder raises this when a post-user message (e.g. an assistant summary) has neither field.

Source

Thrown at python/sglang/srt/entrypoints/openai/encoding_dsv32.py:277

        tool_calls_content = ""
        if tool_calls:
            tool_calls = [
                tool_call_template.format(
                    dsml_token=dsml_token,
                    name=tool_call.get("name"),
                    arguments=encode_arguments_to_dsml(tool_call),
                )
                for tool_call in tool_calls
            ]
            tool_calls_content += "\n\n" + tool_calls_template.format(
                dsml_token=dsml_token, tool_calls="\n".join(tool_calls)
            )

        summary_content = content or ""

        if thinking_mode == "thinking" and index > last_user_idx:
            if not (reasoning_content or tool_calls):
                raise DS32EncodingError(
                    f"ThinkingMode: {thinking_mode}, invalid message without reasoning_content/tool_calls `{msg}` after last user message"
                )
            thinking_part = (
                thinking_template.format(reasoning_content=reasoning_content or "")
                + thinking_end_token
            )

        prompt += assistant_msg_template.format(
            reasoning=thinking_part,
            content=summary_content,
            tool_calls=tool_calls_content,
        )
    else:
        raise NotImplementedError(f"Unknown role: {role}")

    return prompt

View on GitHub (pinned to 0132848349)

Solutions

  1. Use thinking_mode='chat' for plain multi-turn conversations.
  2. Populate reasoning_content (can be a short placeholder) on post-user assistant messages.
  3. Use drop_thinking_messages()/the provided preprocessing to strip or fix history before encoding.

Example fix

# before
{"role":"assistant","content":"answer"}  # after last user msg, thinking mode
# after
{"role":"assistant","reasoning_content":"...","content":"answer"}
Defensive patterns

Strategy: fallback

Validate before calling

if thinking_mode=='thinking':
    for m in messages[last_user_idx+1:]:
        if m['role']=='assistant' and not (m.get('reasoning_content') or m.get('tool_calls')):
            m.setdefault('reasoning_content','(none)')

Prevention

When it happens

Trigger: thinking_mode='thinking' with a trailing assistant message lacking both 'reasoning_content' and 'tool_calls' (typical for multi-turn history in reasoning mode).

Common situations: Sending plain multi-turn history to a thinking-mode model; clients that drop reasoning_content to save tokens; upgrading a chat flow to thinking mode without regenerating history.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/24c81d4842d7b74d. Report an issue: GitHub.