A high-throughput and memory-efficient inference and serving engine for LLMs
[Bugfix] Reject structured outputs for diffusion decoders with a clear error (#45468)
Signed-off-by: Wayne Chiu <waynehacking8@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
WEI CHENG CHIU committed
Jun 14, 2026 at 03:04 UTC
521b88c29ef29b37efefa64efda7b59ec99d210c
2 files changed
+66
-1
tests/v1/structured_output/test_validation.py
new
+50
@@ -0,0 +1,50 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+"""Request-time validation of structured output requests."""
4
+
5
+import pytest
6
+
7
+from vllm.config import StructuredOutputsConfig
8
+from vllm.sampling_params import SamplingParams, StructuredOutputsParams
9
+
10
+pytestmark = pytest.mark.cpu_test
11
+
12
+JSON_SCHEMA = {
13
+ "type": "object",
14
+ "properties": {
15
+ "invoice_id": {"type": "string"},
16
+ "customer": {"type": "string"},
17
+ },
18
+ "required": ["invoice_id", "customer"],
19
+ "additionalProperties": False,
20
+}
21
+
22
+
23
+class _StubModelConfig:
24
+ def __init__(self, is_diffusion: bool):
25
+ self.is_diffusion = is_diffusion
26
+
27
+
28
+def test_structured_outputs_rejected_for_diffusion_models():
29
+ """Diffusion LLMs denoise the canvas in parallel, which is incompatible
30
+ with the token-by-token grammar FSM. The request must fail with a clear
31
+ validation error instead of an FSM rejection mid-generation (#45436)."""
32
+ params = SamplingParams(
33
+ structured_outputs=StructuredOutputsParams(json=JSON_SCHEMA)
34
+ )
35
+ with pytest.raises(ValueError, match="not yet supported for diffusion"):
36
+ params._validate_structured_outputs(
37
+ _StubModelConfig(is_diffusion=True),
38
+ StructuredOutputsConfig(),
39
+ tokenizer=None,
40
+ )
41
+
42
+
43
+def test_plain_request_allowed_for_diffusion_models():
44
+ """Requests without structured outputs are unaffected by the guard."""
45
+ params = SamplingParams()
46
+ params._validate_structured_outputs(
47
+ _StubModelConfig(is_diffusion=True),
48
+ StructuredOutputsConfig(),
49
+ tokenizer=None,
50
+ )
vllm/sampling_params.py
+16
-1
@@ -720,7 +720,9 @@ class SamplingParams(
720
self._validate_logits_processors(model_config)
721
self._validate_allowed_token_ids(tokenizer)
722
self._validate_spec_decode(speculative_config)
723
- self._validate_structured_outputs(structured_outputs_config, tokenizer)
723
+ self._validate_structured_outputs(
724
+ model_config, structured_outputs_config, tokenizer
725
+ )
726
727
def _validate_logprobs(self, model_config: ModelConfig) -> None:
728
max_logprobs = model_config.max_logprobs
@@ -853,12 +855,25 @@ class SamplingParams(
855
856
def _validate_structured_outputs(
857
self,
858
+ model_config: ModelConfig,
859
structured_outputs_config: StructuredOutputsConfig | None,
860
tokenizer: TokenizerLike | None,
861
) -> None:
862
if structured_outputs_config is None or self.structured_outputs is None:
863
return
864
865
+ if model_config.is_diffusion:
866
+ # Diffusion LLMs denoise a whole canvas of tokens in parallel
867
+ # rather than sampling left-to-right, which the grammar FSM
868
+ # requires. Without this check, requests fail mid-generation
869
+ # with an FSM rejection (HTTP 500). See issue #45436.
870
+ raise ValueError(
871
+ "Structured outputs are not yet supported for diffusion "
872
+ "language models. Remove the structured output constraint "
873
+ "(e.g. `response_format`, `structured_outputs`) from the "
874
+ "request."
875
+ )
876
+
877
if tokenizer is None:
878
raise ValueError(
879
"Structured outputs requires a tokenizer so it can't be used with 'skip_tokenizer_init'" # noqa: E501