[Bugfix][Frontend] Raise VLLMValidationError for user-facing errors in chat_utils.py (#50491)

Signed-off-by: latent-9 <296084221+latent-9@users.noreply.github.com> Co-authored-by: latent-9 <296084221+latent-9@users.noreply.github.com>

Recoordinate committed Jul 31, 2026 at 23:54 UTC 0b5b49dde81ca27b27b010fb257138686b056aba
3 files changed +13 -7
tests/entrypoints/unit_tests/test_chat_utils.py
+1 -1
@@ -2092,7 +2092,7 @@ def test_parse_chat_messages_multiple_images_interleave_with_placeholders(
2092 image_url,
2093 ):
2094 with pytest.raises(
2095 - ValueError,
2095 + VLLMValidationError,
2096 match=r"Found more '<|image_1|>' placeholders in input prompt "
2097 "than actual multimodal data items.",
2098 ):
tests/renderers/test_chat_utils_prompt_embeds.py
+1 -1
@@ -316,7 +316,7 @@ _PLACEHOLDER_ERROR_PATTERN: Final[str] = re.sub(
316 def test_parse_chat_messages_rejects_placeholder_in_user_text(content):
317 mc = _make_mock_model_config() # enable_prompt_embeds=True by default
318 messages = [{"role": "user", "content": content}]
319 - with pytest.raises(ValueError, match=_PLACEHOLDER_ERROR_PATTERN):
319 + with pytest.raises(VLLMValidationError, match=_PLACEHOLDER_ERROR_PATTERN):
320 parse_chat_messages(messages, mc, content_format="openai")
321
322
vllm/entrypoints/chat_utils.py
+11 -5
@@ -467,7 +467,7 @@ def _merge_embeds(
467
468 first_keys = set(data_items[0].keys())
469 if any(set(item.keys()) != first_keys for item in data_items[1:]):
470 - raise ValueError(
470 + raise VLLMValidationError(
471 "All dictionaries in the list of embeddings must have the same keys."
472 )
473
@@ -746,9 +746,15 @@ def _resolve_items(
746 modality requires a processor, enforced by the guard below.
747 """
748 if "image" in items_by_modality and "image_embeds" in items_by_modality:
749 - raise ValueError("Mixing raw image and embedding inputs is not allowed")
749 + raise VLLMValidationError(
750 + "Mixing raw image and embedding inputs is not allowed",
751 + parameter="image_embeds",
752 + )
753 if "audio" in items_by_modality and "audio_embeds" in items_by_modality:
751 - raise ValueError("Mixing raw audio and embedding inputs is not allowed")
754 + raise VLLMValidationError(
755 + "Mixing raw audio and embedding inputs is not allowed",
756 + parameter="audio_embeds",
757 + )
758 # `prompt_embeds` bypasses HF MM processors. Every other modality requires one.
759 processor_modalities = items_by_modality.keys() - {"prompt_embeds"}
760 if processor_modalities and mm_processor is None:
@@ -1448,7 +1454,7 @@ def _get_full_multimodal_text_prompt(
1454 interleave_strings,
1455 )
1456 logger.debug("Input prompt: %s", text_prompt)
1451 - raise ValueError(
1457 + raise VLLMValidationError(
1458 f"Found more '{placeholder}' placeholders in input prompt than "
1459 "actual multimodal data items."
1460 )
@@ -1696,7 +1702,7 @@ def _reject_reserved_placeholder_in_text(text: str, model_config: ModelConfig) -
1702 caller move or inject splice positions via plain text content.
1703 """
1704 if model_config.enable_prompt_embeds and PROMPT_EMBEDS_PLACEHOLDER_TOKEN in text:
1699 - raise ValueError(
1705 + raise VLLMValidationError(
1706 _RESERVED_PLACEHOLDER_IN_TEXT_ERROR.format(
1707 token=PROMPT_EMBEDS_PLACEHOLDER_TOKEN
1708 )