sgl-project/sglang · error · ValueError

Inkling thinking parts require role='assistant'

Error message

Inkling thinking parts require role='assistant'

What it means

The Inkling renderer encodes content parts in message order; 'thinking' parts are only meaningful for assistant messages. If a message with role 'tool' contains a thinking part, render_inkling_messages raises this ValueError before encoding.

Source

Thrown at python/sglang/srt/parser/inkling_renderer.py:113

        )

    for message_index, message in enumerate(message_list):
        if message_index == leading_system_count:
            append_effort()
        role = _expect_role(message)
        if role == "tool":
            tool_name = str(
                message.get("name")
                or tool_call_id_to_name.get(message.get("tool_call_id") or "", "")
            )
            # The MM processor harvests media from tool messages, so coercing content
            # to one string would drop images and desync the placeholder count.
            tool_parts = list(_iter_render_parts(message.get("content", "")))
            if not tool_parts:
                tool_parts = [("text", "")]  # else the answered tool_call dangles
            for kind, text in tool_parts:
                if kind == "thinking":
                    raise ValueError("Inkling thinking parts require role='assistant'")
                _append_message(
                    input_ids,
                    tokenizer,
                    "tool",
                    kind,
                    text,
                    author_name=tool_name,
                )
            continue

        parts = list(_iter_render_parts(message.get("content", "")))
        turn_start = len(input_ids)
        if role == "assistant":
            reasoning_content = message.get("reasoning_content")
            if reasoning_content:
                if not isinstance(reasoning_content, str):
                    raise TypeError(
                        "assistant reasoning_content must be a string for Inkling rendering"

View on GitHub (pinned to 0132848349)

Solutions

  1. Remove thinking/reasoning parts from tool and user messages
  2. Move reasoning content into the assistant message's reasoning_content field

Example fix

# before
{"role":"tool","content":[{"type":"thinking","thinking":"hmm"},{"type":"text","text":"42"}]}
# after
{"role":"tool","content":[{"type":"text","text":"42"}]}
Defensive patterns

Strategy: validation

Validate before calling

for m in messages:
    if m.get("role") != "assistant":
        assert not any(isinstance(p, dict) and p.get("type") in ("thinking","reasoning") for p in (m.get("content") or []) if not isinstance(p, str))

Type guard

def no_thinking_in_non_assistant(m):
    if m.get("role") == "assistant": return True
    c = m.get("content", "")
    return isinstance(c, str) or not any(isinstance(p, dict) and p.get("type") in ("thinking","reasoning") for p in c)

Prevention

When it happens

Trigger: Sending a tool-role message whose content includes a part with "type":"thinking" or "reasoning" when using an Inkling-format model.

Common situations: Replaying recorded conversations that mixed reasoning parts into tool outputs; client code that attaches thinking parts to every message generically.

Related errors


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