sgl-project/sglang · error · ValueError

Reasoning parser '{self.reasoning_parser}' is always-on and

Error message

Reasoning parser '{self.reasoning_parser}' is always-on and cannot be disabled via Anthropic thinking

What it means

The request tried to disable thinking (Anthropic thinking enabled=False) for a reasoning parser that SGLang classifies as always-on (e.g. its config special_case == "always" or the default mode resolves to "always"). Such models always produce reasoning and cannot be turned off, so disabling raises instead of silently failing.

Source

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

            # Effort-conditioned, not toggled: "none" (0.0) is the off switch.
            if not enabled:
                request.reasoning_effort = "none"
                return

        config = self.template_manager.reasoning_config
        is_mistral = (config is not None and config.special_case == "mistral") or (
            config is None and self._reasoning_default_mode() == "mistral"
        )
        if is_mistral:
            request.reasoning_effort = "medium" if enabled else "none"
            return

        is_always_on = (config is not None and config.special_case == "always") or (
            config is None and self._reasoning_default_mode() == "always"
        )
        if is_always_on:
            if not enabled:
                raise ValueError(
                    f"Reasoning parser '{self.reasoning_parser}' is always-on "
                    f"and cannot be disabled via Anthropic thinking"
                )
            return

        toggle_param = self._get_reasoning_toggle_param()
        # The read side (``_get_reasoning_from_request``) returns False
        # whenever ``config.toggle_param is None`` OR
        # ``config.default_enabled is None``. The write side must mirror
        # both conditions: if ``default_enabled`` is unset we cannot
        # actually honor an ``enabled=True`` request even when the toggle
        # name itself is resolvable, so writing the kwarg would set up the
        # template to emit reasoning tokens while the parser ignores them
        # (literal ``<think>`` markers leak into the assistant text).
        config = self.template_manager.reasoning_config
        read_side_supported = toggle_param is not None and (
            config is None or config.default_enabled is not None
        )

View on GitHub (pinned to 0132848349)

Solutions

  1. Omit the thinking field entirely for always-on reasoning models
  2. Switch to a model whose reasoning is toggleable if disabling is a hard requirement
  3. Check the parser config's special_case/default mode to know which models are always-on

Example fix

# before
request.thinking = {"type": "disabled"}  # always-on parser
# after
request.thinking = None
Defensive patterns

Strategy: validation

Validate before calling

if thinking is not None and thinking.get('type') == 'disabled' and model_is_always_on_reasoner:
    thinking = None

Type guard

def is_always_on(parser_name) -> bool: return parser_name in ALWAYS_ON_PARSERS  # e.g. per docs

Try / catch

except ValueError as e: if 'always-on' in str(e): drop thinking param and resend

Prevention

When it happens

Trigger: Anthropic-style request with thinking disabled against an always-on reasoning model (certain hybrid/always-thinking model configs).

Common situations: Clients that send thinking={"type":"disabled"} by default for cost control; switching a client from a toggleable reasoner to an always-on one.

Related errors


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