[Bugfix] Return HTTP 400 instead of 501 for unknown chat roles in DeepSeek encoders (#53071)

Signed-off-by: JC-ut0 <809602657@qq.com>

JC-ut0 committed Aug 20, 2026 at 09:23 UTC 38e9cefdefe2e3562ceb6e8decf5101fd6d5c15c
4 files changed +29 -2
tests/test_request_input_bounds.py
+16
@@ -271,3 +271,19 @@ def test_encode_messages_preserves_small_chat_prompt(encoding_module):
271 "<|begin▁of▁sentence|><|User|>Hello<|Assistant|></think>"
272 "Hi<|end▁of▁sentence|>Again<|end▁of▁sentence|>"
273 )
274 +
275 +
276 +@pytest.mark.parametrize(
277 + "encoding_module",
278 + ENCODING_MODULES,
279 + ids=["deepseek_v32", "deepseek_v4"],
280 +)
281 +def test_encode_messages_unknown_role_raises_value_error(encoding_module):
282 + # An invalid role (e.g. uppercase "SYSTEM") is a client error and must be
283 + # raised as ValueError so the OpenAI serving layer maps it to HTTP 400
284 + # instead of NotImplementedError, which would map to HTTP 501.
285 + with pytest.raises(ValueError, match="Invalid role: SYSTEM"):
286 + encoding_module.encode_messages(
287 + [{"role": "SYSTEM", "content": "Hello"}],
288 + thinking_mode="chat",
289 + )
tests/tokenizers_/test_deepseek_v4.py
+11
@@ -113,6 +113,17 @@ def test_deepseek_v4_explicitly_disables_thinking(kwargs):
113 assert prompt == ("<|begin▁of▁sentence|><|User|>Hello<|Assistant|></think>")
114
115
116 +def test_deepseek_v4_unknown_role_raises_value_error():
117 + # Invalid roles are client errors: they must surface as ValueError
118 + # (mapped to HTTP 400 by the OpenAI serving layer), not
119 + # NotImplementedError (mapped to HTTP 501).
120 + with pytest.raises(ValueError, match="Invalid role: SYSTEM"):
121 + _tokenizer().apply_chat_template(
122 + [{"role": "SYSTEM", "content": "Hello"}],
123 + tokenize=False,
124 + )
125 +
126 +
127 def test_deepseek_v4_uses_v4_tool_prompt_from_request_tools():
128 tools = [
129 {
vllm/tokenizers/deepseek_v32_encoding.py
+1 -1
@@ -258,7 +258,7 @@ def render_message(
258 tool_calls=tool_calls_content,
259 )
260 else:
261 - raise NotImplementedError(f"Unknown role: {role}")
261 + raise ValueError(f"Invalid role: {role}")
262
263 return prompt
264
vllm/tokenizers/deepseek_v4_encoding.py
+1 -1
@@ -353,7 +353,7 @@ def render_message(
353 tool_calls=tc_content,
354 )
355 else:
356 - raise NotImplementedError(f"Unknown role: {role}")
356 + raise ValueError(f"Invalid role: {role}")
357
358 # Append transition tokens based on what follows
359 if index + 1 < len(messages) and messages[index + 1].get("role") not in ["assistant", "latest_reminder"]: