From 43b4aaea3e40e20ef53ce2bfecfa1b72060bf2b5 Mon Sep 17 00:00:00 2001 From: Sriraj <168966222+sricursion@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:25:34 +0530 Subject: [PATCH] [Bugfix][Frontend] Return 4xx for client errors in /cohere/v2/chat (#60309) Signed-off-by: sricursion Co-authored-by: Claude --- tests/entrypoints/cohere/test_api_router.py | 28 ++++++++++++++++----- vllm/entrypoints/cohere/api_router.py | 23 ++++++----------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/tests/entrypoints/cohere/test_api_router.py b/tests/entrypoints/cohere/test_api_router.py index 1bb2151621..209ceb4e3e 100644 --- a/tests/entrypoints/cohere/test_api_router.py +++ b/tests/entrypoints/cohere/test_api_router.py @@ -50,7 +50,7 @@ from vllm.entrypoints.serve.exception_handling.handlers.validation import ( from vllm.entrypoints.serve.exception_handling.handlers.vllm_error import ( vllm_error_handler, ) -from vllm.exceptions import VLLMError +from vllm.exceptions import VLLMError, VLLMValidationError from vllm.sampling_params import SamplingParams @@ -78,7 +78,7 @@ class _Handler: * a :class:`CohereChatV2Response` (non-streaming JSON path); * an async generator yielding SSE frames (streaming path); * an :class:`ErrorResponse` (error envelope path); or - * an exception (router-level 500 path). + * an exception (router-level error path). """ def __init__(self, result): @@ -371,6 +371,22 @@ class TestEndpoint: body = r.json() assert body == {"message": "kaboom"} + @pytest.mark.parametrize( + "exc", + [ + VLLMValidationError("bad temperature", parameter="temperature"), + ValueError("bad temperature"), + ], + ids=["vllm_validation_error", "value_error"], + ) + def test_handler_client_error_returns_400_envelope(self, exc): + """Client-caused handler errors are 4xx, matching the OpenAI routes.""" + app = _build_app(handler=_Handler(exc)) + with TestClient(app) as client: + r = client.post("/cohere/v2/chat", json=_minimal_request_body()) + assert r.status_code == HTTPStatus.BAD_REQUEST + assert "bad temperature" in r.json()["message"] + def test_non_json_content_type_rejected(self): """The ``validate_json_request`` dependency raises ``RequestValidationError`` (HTTP 422) for non-JSON content @@ -525,10 +541,10 @@ class TestRenderEndpoint: assert r.status_code == HTTPStatus.INTERNAL_SERVER_ERROR assert r.json() == {"message": "tokenizer exploded"} - def test_conversion_exception_returns_500_envelope(self): + def test_conversion_client_error_returns_400_envelope(self): """Conversion runs inside the same ``try`` as the render call, so - a bad v2 body that slips past Pydantic still yields the Cohere - error envelope rather than an unhandled 500 with vLLM's shape. + a bad v2 body that slips past Pydantic still yields a 4xx in the + Cohere error envelope rather than an unhandled 500 with vLLM's shape. """ class _Boom: @@ -538,7 +554,7 @@ class TestRenderEndpoint: app = _build_render_app(_Boom(), _RenderHandler(_generate_request())) with TestClient(app) as client: r = client.post("/cohere/v2/chat/render", json=_minimal_request_body()) - assert r.status_code == HTTPStatus.INTERNAL_SERVER_ERROR + assert r.status_code == HTTPStatus.BAD_REQUEST assert r.json() == {"message": "unconvertible message"} def test_invalid_body_returns_422(self): diff --git a/vllm/entrypoints/cohere/api_router.py b/vllm/entrypoints/cohere/api_router.py index db6366df71..5362b1d9e9 100644 --- a/vllm/entrypoints/cohere/api_router.py +++ b/vllm/entrypoints/cohere/api_router.py @@ -40,6 +40,9 @@ from starlette.middleware.base import BaseHTTPMiddleware import vllm.envs as envs from vllm.entrypoints.serve.engine.protocol import ErrorResponse +from vllm.entrypoints.serve.exception_handling.error_response import ( + create_error_response, +) from vllm.entrypoints.serve.exception_handling.utils import sanitize_message from vllm.entrypoints.serve.utils.api_utils import ( load_aware_call, @@ -138,15 +141,9 @@ if _SDK_AVAILABLE: try: result = await handler.create_chat_v2(request, raw_request) - except Exception as e: # noqa: BLE001 - report as 500 for parity + except Exception as e: logger.exception("Error in /cohere/v2/chat: %s", e) - return JSONResponse( - status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value, - content=CohereError( - message=sanitize_message(str(e)), - id=_request_id(raw_request), - ).model_dump(exclude_none=True), - ) + return _error_response(create_error_response(e), raw_request) match result: case ErrorResponse(): @@ -194,15 +191,9 @@ if _SDK_AVAILABLE: try: chat_request = handler.to_chat_completion_request(request) result = await render_handler.render_chat_request(chat_request) - except Exception as e: # noqa: BLE001 - report as 500 for parity + except Exception as e: logger.exception("Error in /cohere/v2/chat/render: %s", e) - return JSONResponse( - status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value, - content=CohereError( - message=sanitize_message(str(e)), - id=_request_id(raw_request), - ).model_dump(exclude_none=True), - ) + return _error_response(create_error_response(e), raw_request) if isinstance(result, ErrorResponse): return _error_response(result, raw_request)