sgl-project/sglang · error · ValueError

Cannot rewrap thinking history: no reasoning detector is con

Error message

Cannot rewrap thinking history: no reasoning detector is configured for this model

What it means

A chat request contains assistant reasoning/thinking blocks in history, and the server needs to re-wrap them with the model's think tokens (e.g. <think>), but no reasoning parser is configured for the served model. Since silently flattening thinking text into ordinary content would leak reasoning into a non-reasoning model, the server raises instead.

Source

Thrown at python/sglang/srt/entrypoints/openai/serving_chat.py:2330

    def supports_native_reasoning_history(self) -> bool:
        """Whether the chat encoder takes history as ``reasoning_content`` rather
        than via :meth:`wrap_reasoning_history`; see
        :func:`chat_encoding.spec_owns_reasoning_history` for why.
        """
        return chat_encoding.spec_owns_reasoning_history(self.chat_encoding_spec)

    def wrap_reasoning_history(self, reasoning_text: str) -> str:
        """Wrap prior-turn reasoning in the detector's own start/end tokens.

        Pulling the delimiters from the detector keeps adapters in lockstep
        with any future parser that ships non-``<think>`` markers — Mistral's
        ``[THINK]``, Gemma4's ``think_start_self_label = "thought\\n"``, etc.
        Falling back to a plain string is unsafe: it would let prior
        thinking text reach a non-reasoning model as ordinary assistant
        content, so the caller must surface this state, not paper over it.
        """
        if self._reasoning_detector is None:
            raise ValueError(
                "Cannot rewrap thinking history: no reasoning detector is "
                "configured for this model"
            )
        d = self._reasoning_detector
        return (
            f"{d.think_start_token}{d.think_start_self_label}"
            f"{reasoning_text}\n{d.think_end_token}"
        )

    def _reasoning_default_mode(self) -> Optional[str]:
        if self._reasoning_detector is None:
            return None
        return self._reasoning_detector.reasoning_default

    def _get_reasoning_toggle_param(self) -> Optional[str]:
        """Resolve the chat-template kwarg that toggles reasoning, if any."""
        config = self.template_manager.reasoning_config
        if config is not None:

View on GitHub (pinned to 0132848349)

Solutions

  1. Launch the server with --reasoning-parser matching the model (e.g. deepseek-r1, qwen3) so a detector exists
  2. Strip reasoning_content / thinking blocks from assistant history before sending to a non-reasoning model
  3. If the target model is a reasoner, verify the correct parser name (see sglang ReasoningParser registry)

Example fix

# before
messages=[{"role":"assistant","content":"ans","reasoning_content":"chain..."}]  # to non-reasoning model
# after
messages=[{"role":"assistant","content":"ans"}]
Defensive patterns

Strategy: validation

Validate before calling

def clean_history(messages):
    return [{k: v for k, v in m.items() if k != 'reasoning_content'} for m in messages]

Type guard

def has_reasoning_history(messages) -> bool:
    return any(m.get('reasoning_content') or m.get('thinking_blocks') for m in messages if m.get('role') == 'assistant')

Prevention

When it happens

Trigger: Calling /v1/chat/completions (or Anthropic-style API) with prior assistant messages containing reasoning_content blocks while the server was launched without --reasoning-parser (so self._reasoning_detector is None).

Common situations: Replaying a captured conversation (from a reasoning model) against a non-reasoning model; forgetting to set --reasoning-parser deepseek-r1/qwen3 etc.; migrating clients that persist reasoning_content in history.

Related errors


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