# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Tests that validation_exception_handler populates the `param` field in its error response using the Pydantic error's `loc`, even when no custom VLLMValidationError context is present. Previously, `param` was only populated for errors carrying a custom VLLMValidationError in their Pydantic `ctx`. Plain validation failures (missing fields, wrong types) left `param` as None, even though the field name was readily available from `error['loc']`. """ import json from types import SimpleNamespace import pytest from fastapi.exceptions import RequestValidationError from vllm.entrypoints.serve.utils.server_utils import validation_exception_handler def _fake_request(log_error_stack: bool = False) -> SimpleNamespace: """Minimal stand-in for a FastAPI Request - just enough for the handler to read req.app.state.args.log_error_stack.""" return SimpleNamespace( app=SimpleNamespace( state=SimpleNamespace(args=SimpleNamespace(log_error_stack=log_error_stack)) ), state=SimpleNamespace(), # no request_metadata -> hasattr(...) is False ) class TestValidationErrorParamFallback: """Ensure `param` falls back to the Pydantic error's `loc` when no custom VLLMValidationError context is present.""" @pytest.mark.parametrize( ("error_type", "msg"), [ ("missing", "Field required"), ("list_type", "Input should be a valid list"), ], ids=["missing-field", "wrong-type"], ) @pytest.mark.asyncio async def test_param_falls_back_to_loc(self, error_type: str, msg: str): errors = [{"type": error_type, "loc": ("body", "messages"), "msg": msg}] exc = RequestValidationError(errors) response = await validation_exception_handler(_fake_request(), exc) body = json.loads(response.body) assert body["error"]["param"] == "body.messages" @pytest.mark.asyncio async def test_param_fallback_does_not_crash_on_non_dict_error(self): """Schemathesis fuzzing found that errors[0] isn't always a dict. The fallback must not crash in that case - it should just leave param as None instead of raising.""" exc = RequestValidationError(["some unexpected non-dict error"]) response = await validation_exception_handler(_fake_request(), exc) body = json.loads(response.body) assert body["error"]["param"] is None