From 5965648547b016e5993cf8bb2ec4d1b3c447d2c8 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sun, 16 Aug 2026 14:40:37 -0700 Subject: [PATCH] fix(proxy): close websocket cleanly when OpenAI credentials are missing --- .../llm_passthrough_endpoints.py | 7 +++-- .../test_openai_ws_passthrough_routes.py | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py index 96e32ccf827..8cdcdc07547 100644 --- a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py @@ -2025,8 +2025,11 @@ async def openai_websocket_proxy_route( region_name=None, ) if openai_api_key is None: - await websocket.close(code=1011) - raise ValueError("Required 'OPENAI_API_KEY' in environment to make pass-through calls to OpenAI.") + await websocket.close( + code=1011, + reason="Required 'OPENAI_API_KEY' in environment to make pass-through calls to OpenAI.", + ) + return raw_path: Final = httpx.URL(endpoint).path encoded_endpoint: Final = raw_path if raw_path.startswith("/") else f"/{raw_path}" diff --git a/tests/test_litellm/proxy/test_openai_ws_passthrough_routes.py b/tests/test_litellm/proxy/test_openai_ws_passthrough_routes.py index 82422d35b9f..b22e202d9e0 100644 --- a/tests/test_litellm/proxy/test_openai_ws_passthrough_routes.py +++ b/tests/test_litellm/proxy/test_openai_ws_passthrough_routes.py @@ -94,6 +94,32 @@ async def test_openai_websocket_accepts_first_client_subprotocol(): websocket.close.assert_not_awaited() +@pytest.mark.asyncio +async def test_openai_websocket_closes_cleanly_when_provider_credentials_missing(): + websocket = _mock_websocket("/openai/v1/realtime", "model=gpt-4o-realtime-preview") + + with ( + patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials", + return_value=None, + ), + patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.websocket_passthrough_request", + new_callable=AsyncMock, + ) as mock_ws, + ): + await openai_websocket_proxy_route( + websocket=websocket, + endpoint="v1/realtime", + user_api_key_dict=UserAPIKeyAuth(), + ) + + websocket.close.assert_awaited_once() + assert websocket.close.await_args.kwargs["code"] == 1011 + websocket.accept.assert_not_awaited() + mock_ws.assert_not_awaited() + + @pytest.mark.asyncio @pytest.mark.parametrize( "user_api_key_dict",