-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Description
What happened?
A bug happened!
Bug Summary
When using the LiteLLM SDK's embedding() function with Gemini models and a custom api_base parameter (e.g., for Cloudflare AI Gateway routing), the authentication header is not included in the HTTP request, causing authentication failures.
================================================================================
BUG #1: Missing Authorization Header (x-goog-api-key)
EXPECTED BEHAVIOR:
When api_key is provided, request should include:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-goog-api-key': 'AIzaSyAS0bSJ2TaEvFte...' ← SHOULD BE SENT
}
ACTUAL BEHAVIOR:
Request only includes:
headers = {
'Content-Type': 'application/json; charset=utf-8'
# ← x-goog-api-key is MISSING!
}
Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new
LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.
✅ BUG CONFIRMED: Request failed with authentication error
Error: litellm.BadRequestError: GeminiException BadRequestError - {
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (...
================================================================================
BUG #2: Ignored Extra Headers Parameter
EXPECTED BEHAVIOR:
When extra_headers is provided, request should include:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'X-Custom-Header': 'test-value', ← SHOULD BE SENT
'Authorization': 'Bearer token' ← SHOULD BE SENT
}
ACTUAL BEHAVIOR:
extra_headers parameter is completely ignored
Request only includes Content-Type header
Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new
LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.
✅ BUG CONFIRMED: extra_headers were ignored
If extra_headers worked, Authorization header would be sent
Error: litellm.BadRequestError: GeminiException BadRequestError - {
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (...
================================================================================
ROOT CAUSE
File: litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py
Method: GoogleBatchEmbeddings.batch_embeddings()
Lines 60-62 create headers WITHOUT including auth_header:
headers = {'Content-Type': 'application/json; charset=utf-8'}
← auth_header is retrieved but NEVER added!
← extra_headers parameter is NEVER checked!
================================================================================
FIX (2 lines)
headers = {'Content-Type': 'application/json; charset=utf-8'}
if auth_header:
headers.update(auth_header)
Environment
- LiteLLM Version: 1.80.10 (latest as of 2025-12-15)
- Python Version: 3.13
- Provider: Google Gemini (gemini/gemini-embedding-001)
- Use Case: Routing through Cloudflare AI Gateway (CFAIG)
Steps to Reproduce
from litellm import embedding
import os
# Cloudflare AI Gateway URL (noauth mode)
cfaig_url = "https://gateway.ai.cloudflare.com/v1/{account_id}/noauth/google-ai-studio/v1beta"
response = embedding(
model="gemini/gemini-embedding-001",
input=["What is the meaning of life?"],
api_key=os.getenv("GEMINI_API_KEY"),
api_base=cfaig_url,
)Expected Behavior
The request should include the x-goog-api-key header with the Gemini API key, similar to how the completion() function handles Gemini models with custom api_base.
Actual Behavior
The request is sent without any authentication header, resulting in:
httpx.HTTPStatusError: Client error '401 Unauthorized'
litellm.exceptions.AuthenticationError: GeminiException - {"success":false,"result":[],"messages":[],"error":[{"code":2009,"message":"Unauthorized"}]}
Root Cause
File: litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py
Method: GoogleBatchEmbeddings.batch_embeddings()
The bug is in the batch_embeddings() method:
- Line 26-38: The
_get_token_and_url()method correctly retrievesauth_headercontaining the authentication credentials - Line 60-62: Creates a
headersdict but never includes theauth_header - Line 87-91: Sends the HTTP request with incomplete headers (missing authentication)
Problematic Code:
# Line 26-38: Gets auth_header correctly
auth_header, url = self._get_token_and_url(
model=model,
auth_header=_auth_header,
gemini_api_key=api_key,
...
api_base=api_base,
...
)
# Line 60-62: Creates headers WITHOUT including auth_header ❌
headers = {
"Content-Type": "application/json; charset=utf-8",
}
# ❌ BUG: auth_header is never added to headers!
# Line 87-91: Sends request with incomplete headers
response = sync_handler.post(
url=url,
headers=headers, # Missing authentication!
data=json.dumps(request_data),
)Proposed Fix
Add the auth_header to the headers dict before sending the request:
# Line 60-66: Include auth_header in headers ✅
headers = {
"Content-Type": "application/json; charset=utf-8",
}
# ✅ FIX: Add auth_header if it exists
if auth_header:
headers.update(auth_header)Additional Context
- The
completion()function handles this correctly for Gemini models with customapi_base - The
_check_custom_proxy()method (line 46) correctly setsauth_header = {"x-goog-api-key": gemini_api_key}whenapi_baseis provided - This bug affects all use cases where Gemini embeddings are routed through a proxy/gateway with custom
api_base
Impact
This bug prevents users from:
- Routing Gemini embeddings through Cloudflare AI Gateway for caching/analytics
- Using any custom proxy/gateway for Gemini embeddings
- Benefiting from edge caching and cost optimization features
Related Issues
This appears to be related to the fix in PR #16085 which fixed direct Gemini API access, but the fix didn't cover the case when api_base is set.
Relevant log output
================================================================================
BUG #1: Missing Authorization Header (x-goog-api-key)
================================================================================
EXPECTED BEHAVIOR:
When api_key is provided, request should include:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-goog-api-key': 'AIzaSyAS0bSJ2TaEvFte...' ← SHOULD BE SENT
}
ACTUAL BEHAVIOR:
Request only includes:
headers = {
'Content-Type': 'application/json; charset=utf-8'
# ← x-goog-api-key is MISSING!
}
Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new
LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.
✅ BUG CONFIRMED: Request failed with authentication error
Error: litellm.BadRequestError: GeminiException BadRequestError - {
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (...
================================================================================
BUG #2: Ignored Extra Headers Parameter
================================================================================
EXPECTED BEHAVIOR:
When extra_headers is provided, request should include:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'X-Custom-Header': 'test-value', ← SHOULD BE SENT
'Authorization': 'Bearer token' ← SHOULD BE SENT
}
ACTUAL BEHAVIOR:
extra_headers parameter is completely ignored
Request only includes Content-Type header
Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new
LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.
✅ BUG CONFIRMED: extra_headers were ignored
If extra_headers worked, Authorization header would be sent
Error: litellm.BadRequestError: GeminiException BadRequestError - {
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (...
================================================================================
ROOT CAUSE
================================================================================
File: litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py
Method: GoogleBatchEmbeddings.batch_embeddings()
Lines 60-62 create headers WITHOUT including auth_header:
headers = {'Content-Type': 'application/json; charset=utf-8'}
# ← auth_header is retrieved but NEVER added!
# ← extra_headers parameter is NEVER checked!
================================================================================
FIX (2 lines)
================================================================================
headers = {'Content-Type': 'application/json; charset=utf-8'}
if auth_header:
headers.update(auth_header)
================================================================================Are you a ML Ops Team?
No
What LiteLLM version are you on ?
v1.80.10
Twitter / LinkedIn details
No response