From d4f515e3f226c0d2568be3c996e9d825e2e23833 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Tue, 2 Jun 2026 12:05:16 -0700 Subject: [PATCH] fix(vector-stores): raise BadRequestError (400) for invalid Vertex Search extra_body Rejecting unsupported or target-selecting extra_body fields previously raised a bare ValueError, which the vector store error path mapped to a generic APIConnectionError (HTTP 500). Raise litellm.BadRequestError so invalid per-request input surfaces as HTTP 400 with a clear message. --- .../search_api/transformation.py | 34 ++++++++++++------- ...x_ai_search_vector_store_transformation.py | 18 +++++++--- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/litellm/llms/vertex_ai/vector_stores/search_api/transformation.py b/litellm/llms/vertex_ai/vector_stores/search_api/transformation.py index 2e55aed3c0b..46dedb3d0a4 100644 --- a/litellm/llms/vertex_ai/vector_stores/search_api/transformation.py +++ b/litellm/llms/vertex_ai/vector_stores/search_api/transformation.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union import httpx from litellm import get_model_info +from litellm.exceptions import BadRequestError from litellm.litellm_core_utils.url_utils import encode_url_path_segment from litellm.llms.base_llm.vector_store.transformation import BaseVectorStoreConfig from litellm.llms.vertex_ai.vertex_llm_base import VertexBase @@ -85,11 +86,12 @@ class VertexSearchAPIVectorStoreConfig(BaseVectorStoreConfig, VertexBase): Validate ``extra_body`` against the supported-field allowlist for the active serving config (engine/app vs data store). - Raises ``ValueError`` if the caller includes a target-selecting field - (e.g. ``servingConfig``) or any field not supported for the active mode, - so the request fails loudly instead of silently searching the wrong - target. Engine-only fields (``dataStoreSpecs``, ``numResultsPerDataStore``) - are rejected in data-store mode where they are meaningless. + Raises ``BadRequestError`` (HTTP 400) if the caller includes a + target-selecting field (e.g. ``servingConfig``) or any field not + supported for the active mode, so the request fails loudly instead of + silently searching the wrong target. Engine-only fields + (``dataStoreSpecs``, ``numResultsPerDataStore``) are rejected in + data-store mode where they are meaningless. """ supported = cls.get_supported_extra_body_fields(is_engine=is_engine) filtered = { @@ -98,18 +100,26 @@ class VertexSearchAPIVectorStoreConfig(BaseVectorStoreConfig, VertexBase): target_selecting = set(filtered) & VERTEX_SEARCH_TARGET_SELECTING_FIELDS if target_selecting: - raise ValueError( - "Vertex AI Search extra_body may not set target-selecting fields " - f"{sorted(target_selecting)}: the data store is scoped by " - "vector_store_id / vertex_engine_id and cannot be overridden per request." + raise BadRequestError( + message=( + "Vertex AI Search extra_body may not set target-selecting fields " + f"{sorted(target_selecting)}: the data store is scoped by " + "vector_store_id / vertex_engine_id and cannot be overridden per request." + ), + model="vertex_ai/search_api", + llm_provider="vertex_ai", ) unsupported = set(filtered) - supported if unsupported: mode = "engine/app" if is_engine else "data store" - raise ValueError( - f"Unsupported Vertex AI Search extra_body fields {sorted(unsupported)} " - f"for {mode} mode. Supported fields: {sorted(supported)}." + raise BadRequestError( + message=( + f"Unsupported Vertex AI Search extra_body fields {sorted(unsupported)} " + f"for {mode} mode. Supported fields: {sorted(supported)}." + ), + model="vertex_ai/search_api", + llm_provider="vertex_ai", ) return filtered diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_search_vector_store_transformation.py b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_search_vector_store_transformation.py index 8a83ae3a894..034f85f5a0b 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_search_vector_store_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_search_vector_store_transformation.py @@ -2,6 +2,7 @@ from types import SimpleNamespace import pytest +from litellm.exceptions import BadRequestError from litellm.llms.vertex_ai.vector_stores.search_api.transformation import ( VertexSearchAPIVectorStoreConfig, ) @@ -209,32 +210,39 @@ def test_engine_search_request_forwards_num_results_per_data_store(): def test_datastore_search_request_rejects_datastorespecs(): specs = [{"dataStore": "projects/p/.../dataStores/ds-beta"}] - with pytest.raises(ValueError, match="data store mode"): + with pytest.raises(BadRequestError, match="data store mode"): _datastore_search_request(extra_body={"dataStoreSpecs": specs}) def test_datastore_search_request_rejects_num_results_per_data_store(): - with pytest.raises(ValueError, match="data store mode"): + with pytest.raises(BadRequestError, match="data store mode"): _datastore_search_request(extra_body={"numResultsPerDataStore": 3}) @pytest.mark.parametrize("field", ["branch", "servingConfig", "entity"]) def test_search_request_rejects_target_selecting_fields(field): - with pytest.raises(ValueError, match="target-selecting"): + with pytest.raises(BadRequestError, match="target-selecting"): _search_request(extra_body={field: "x"}) @pytest.mark.parametrize("field", ["branch", "servingConfig", "entity"]) def test_datastore_search_request_rejects_target_selecting_fields(field): - with pytest.raises(ValueError, match="target-selecting"): + with pytest.raises(BadRequestError, match="target-selecting"): _datastore_search_request(extra_body={field: "x"}) def test_search_request_rejects_unsupported_extra_body_field(): - with pytest.raises(ValueError, match="Unsupported Vertex AI Search extra_body"): + with pytest.raises(BadRequestError, match="Unsupported Vertex AI Search extra_body"): _search_request(extra_body={"notARealField": True}) +def test_rejected_extra_body_raises_http_400(): + with pytest.raises(BadRequestError) as exc_info: + _search_request(extra_body={"notARealField": True}) + + assert exc_info.value.status_code == 400 + + def test_search_request_forwards_supported_extra_body_fields(): _, body = _search_request( extra_body={