fix(frontend): return 400 instead of 500 when chat template rendering fails - #12404
Conversation
WalkthroughThe OpenAI chat-completions validator now requires at least one user message. Unit and HTTP tests cover non-user histories, streaming modes, status codes, content type, and error payloads. ChangesChat completion validation
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
|
🎯 Code Coverage (details) 🔗 Commit SHA: b642340 | Docs | Datadog PR Page | Give us feedback! |
|
Native vLLM 0.26.0 does not reject the assistant-only request in this PR. I served Example vLLM 0.26.0 serve commandCUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-0.6B --host 127.0.0.1 --port 18105 --dtype half --max-model-len 4096 --gpu-memory-utilization 0.35 --enforce-eagerRequest: curl -i -X POST http://127.0.0.1:18105/v1/chat/completions -H 'Content-Type: application/json' --data-binary '{
"model": "Qwen/Qwen3-0.6B",
"max_tokens": 32,
"messages": [
{
"role": "assistant",
"content": "I am an assistant."
}
]
}'Response (abridged): HTTP/1.1 200 OK
content-type: application/json
{
"model": "Qwen/Qwen3-0.6B",
"choices": [
{
"message": {
"role": "assistant",
"content": "<think>\nOkay, the user says, \"I am an assistant.\" ..."
},
"finish_reason": "length"
}
],
"system_fingerprint": "vllm-0.26.0-nohash",
"usage": {
"prompt_tokens": 13,
"completion_tokens": 32,
"total_tokens": 45
}
}This means the proposed user-role validation is stricter than native vLLM 0.26.0 for this model/template. It would be a Dynamo API policy rather than vLLM parity. |
|
Native SGLang does not reject the default assistant-only request in this PR. I served Example SGLang serve commanddocker run --rm --name sglang-qwen3 --gpus 'device=0' --shm-size 8g -p 127.0.0.1:18106:30000 -v "$HOME/.cache/huggingface:/root/.cache/huggingface" lmsysorg/sglang:nightly-dev-cu13-20260729-16a52bff sglang serve --model-path Qwen/Qwen3-0.6B --host 0.0.0.0 --port 30000 --dtype float16 --context-length 4096 --mem-fraction-static 0.35 --cuda-graph-backend-decode disabled --cuda-graph-backend-prefill disabled --disable-radix-cacheRequest: curl -i -X POST http://127.0.0.1:18106/v1/chat/completions -H 'Content-Type: application/json' --data-binary '{
"model": "Qwen/Qwen3-0.6B",
"max_tokens": 32,
"messages": [
{
"role": "assistant",
"content": "I am an assistant."
}
]
}'Response (abridged): HTTP/1.1 200 OK
content-type: application/json
{
"model": "Qwen/Qwen3-0.6B",
"choices": [
{
"message": {
"role": "assistant",
"content": "<think>\nOkay, the user said, \"I am an assistant.\" Let me think about how to respond. ..."
},
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": 13,
"completion_tokens": 32,
"total_tokens": 45,
"reasoning_tokens": 0
},
"metadata": {
"weight_version": "default"
}
}This is SGLang's default behavior with |
Signed-off-by: Ryan McCormick <rmccormick@nvidia.com> Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
0d7accb to
b642340
Compare
Summary
When a chat template refuses to render a request (for example via
raise_exception), the frontend returns HTTP 500"Failed to generate completions"and drops the template's own error message. The failure is caused by the request, so it should be a client error.This PR classifies every chat template render failure as HTTP 400 and forwards the template's message to the client. vLLM does the same for template render failures. The fix is @rmccorm4's, taken over from
rmccormick/template-error-400:OpenAIPreprocessor::map_prompt_render_errornow maps all render failures toErrorType::InvalidArgument, which the HTTP layer already turns into 400.Before / after (model whose template rejects a history with no user message):
Before:
After:
Notes:
stream: truebehaves the same: a JSON 400, not an SSE stream. Rendering fails before the response stream opens./v1/chat/completions,/v1/responses, and/v1/messages, which all use the same renderer.role:"system"turns fail chat-template rendering #11762 (mid-conversationrole: "system"turns make Qwen and Llama templates raise, surfacing as 500).debuglevel, so a server-side template misconfiguration that fails at render time stays diagnosable.Validation
lib/llm/tests/chat_template_render_errors.rs): drives real HTTP through a real preprocessor and template. A raising template returns 400 with a JSON body for bothstream: falseandstream: true; an accepting template returns 200 for the same assistant-only request.left: 500, right: 400on both streaming modes, so the test covers the regression, not the implementation.