fix(vertex passthrough): return an actionable auth error when Google token minting fails

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
milan 2026-08-13 22:30:26 +00:00
parent 87736a767c
commit ca6f731d80
2 changed files with 78 additions and 16 deletions

View file

@ -1598,23 +1598,35 @@ async def _prepare_vertex_auth_headers(
else:
raise ValueError("No vertex credentials found")
_auth_header, vertex_project = await vertex_llm_base._ensure_access_token_async(
credentials=vertex_credentials_str,
project_id=vertex_project,
custom_llm_provider="vertex_ai_beta",
)
try:
_auth_header, vertex_project = await vertex_llm_base._ensure_access_token_async(
credentials=vertex_credentials_str,
project_id=vertex_project,
custom_llm_provider="vertex_ai_beta",
)
auth_header, _ = vertex_llm_base._get_token_and_url(
model="",
auth_header=_auth_header,
gemini_api_key=None,
vertex_credentials=vertex_credentials_str,
vertex_project=vertex_project,
vertex_location=vertex_location,
stream=False,
custom_llm_provider="vertex_ai_beta",
api_base="",
)
auth_header, _ = vertex_llm_base._get_token_and_url(
model="",
auth_header=_auth_header,
gemini_api_key=None,
vertex_credentials=vertex_credentials_str,
vertex_project=vertex_project,
vertex_location=vertex_location,
stream=False,
custom_llm_provider="vertex_ai_beta",
api_base="",
)
except Exception as e:
raise ProxyException(
message=(
f"Failed to get a Google access token for project={vertex_project} + location={vertex_location}: {e}. "
"Set `vertex_credentials` on the deployment marked `use_in_pass_through: true`, or on "
"`default_vertex_config` / `DEFAULT_GOOGLE_APPLICATION_CREDENTIALS`."
),
type=ProxyErrorTypes.auth_error,
param="vertex_credentials",
code=401,
) from e
# Use allowlist approach - only forward specific safe headers
headers = get_vertex_ai_allowed_incoming_headers(request)

View file

@ -1,6 +1,7 @@
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from google.auth.exceptions import DefaultCredentialsError
from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
_base_vertex_proxy_route,
@ -346,6 +347,55 @@ async def test_vertex_passthrough_forwards_anthropic_beta_header():
assert headers_passed_through is False
@pytest.mark.asyncio
async def test_vertex_passthrough_credential_failure_raises_auth_error():
"""
A project/location configured for pass-through without usable Google credentials used to
bubble google.auth errors out of the route, so callers only saw an opaque 500
{"error": {"message": "Internal server error"}} with no hint about what to configure.
"""
from starlette.datastructures import Headers
from litellm.llms.vertex_ai.vertex_llm_base import VertexBase
from litellm.proxy._types import ProxyErrorTypes, ProxyException
from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
_prepare_vertex_auth_headers,
)
mock_request = MagicMock()
mock_request.headers = Headers({"authorization": "Bearer sk-litellm-key"})
mock_request.state._cached_headers = None
mock_vertex_credentials = MagicMock()
mock_vertex_credentials.vertex_project = "test-project"
mock_vertex_credentials.vertex_location = "us-central1"
mock_vertex_credentials.vertex_credentials = None
with patch.object(
VertexBase,
"_ensure_access_token_async",
new_callable=AsyncMock,
side_effect=DefaultCredentialsError("Your default credentials were not found"),
):
with pytest.raises(ProxyException) as exc_info:
await _prepare_vertex_auth_headers(
request=mock_request,
vertex_credentials=mock_vertex_credentials,
router_credentials=None,
vertex_project="test-project",
vertex_location="us-central1",
base_target_url="https://us-central1-aiplatform.googleapis.com",
get_vertex_pass_through_handler=MagicMock(),
)
assert exc_info.value.code == "401"
assert exc_info.value.type == ProxyErrorTypes.auth_error.value
assert "test-project" in exc_info.value.message
assert "us-central1" in exc_info.value.message
assert "vertex_credentials" in exc_info.value.message
assert "Your default credentials were not found" in exc_info.value.message
@pytest.mark.asyncio
async def test_vertex_passthrough_does_not_forward_litellm_auth_token():
"""