[Bugfix][Structured Outputs] Reject empty `structural_tag` at request validation (#47450)

Signed-off-by: linnea-lin-00638949 <15521435947@163.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Artem Perevedentsev <aperevedents@nvidia.com>

linyafeng committed Sep 22, 2026 at 21:09 UTC e548298f8144cae77d26949078c264d819d3993c
2 files changed +19 -2
tests/v1/structured_output/test_validation.py
+7 -2
@@ -56,13 +56,18 @@ def test_plain_request_allowed_for_diffusion_models():
56 [
57 (StructuredOutputsParams(json_object=False), "json_object must be True"),
58 (StructuredOutputsParams(json=""), "json cannot be an empty string"),
59 + (
60 + StructuredOutputsParams(structural_tag=""),
61 + "structural_tag cannot be an empty string",
62 + ),
63 ],
64 )
65 def test_degenerate_structured_outputs_rejected(structured_outputs, match):
66 """json_object=False and an empty json schema pass the `is not None`
67 exclusivity check but resolve to no structured-output key, so they must be
64 - rejected at request validation (-> 400) instead of reaching and crashing
65 - the engine."""
68 + rejected at request validation (-> 400). Empty `structural_tag` is rejected
69 + for the same reason: `json.loads("")` in `compile_grammar` would otherwise
70 + raise JSONDecodeError and surface as a per-request engine error."""
71 params = SamplingParams(structured_outputs=structured_outputs)
72 with pytest.raises(VLLMValidationError, match=match):
73 params._validate_structured_outputs(
vllm/sampling_params.py
+12
@@ -1144,6 +1144,18 @@ class SamplingParams(
1144 raise VLLMValidationError(
1145 "structured_outputs.regex must not contain a NUL character ('\\x00')"
1146 )
1147 + # Note(arpera):
1148 + # We do NOT check here structured output regex on emptiness because
1149 + # empty regex is indeed compiles to a valid grammar as well as
1150 + # whitespace-only regexps, for instance, regex="\n" or regex=" "
1151 + # are valid patterns and we MUST process them.
1152 + if (
1153 + isinstance(self.structured_outputs.structural_tag, str)
1154 + and self.structured_outputs.structural_tag.strip() == ""
1155 + ):
1156 + raise VLLMValidationError(
1157 + "structured_outputs.structural_tag cannot be an empty string"
1158 + )
1159
1160 from vllm.v1.structured_output.backend_guidance import (
1161 has_guidance_unsupported_json_features,