BerriAI/litellm · error · HTTPException
No Braintrust API token provided. Pass via Authorization hea
Error message
No Braintrust API token provided. Pass via Authorization header or set BRAINTRUST_API_KEY environment variable.
What it means
After resolving the provider config, the handler calls transform_request_image_variation (handler.py:141) and expects the returned mapping to contain a non-empty "data" dict, which is then spread as client.images.create_variation(**json_data). The stock OpenAI config always returns {"data": {"image": image, **optional_params}} (transformation.py:36-41), so this ValueError means the transform produced no usable "data" payload — i.e. a custom, mismatched, or wrong-shape config object was used. Like its sibling error, it is re-raised wrapped in an OpenAIError(status_code=500) by the outer except.
Source
Thrown at cookbook/litellm_proxy_server/braintrust_prompt_wrapper_server.py:190
"""
Fetch a prompt from Braintrust and transform it to LiteLLM format.
Args:
prompt_id: The Braintrust prompt ID
authorization: Bearer token for Braintrust API (from header)
Returns:
JSONResponse with the transformed prompt data
"""
# Extract token from Authorization header or environment
braintrust_token = None
if authorization and authorization.startswith("Bearer "):
braintrust_token = authorization.replace("Bearer ", "")
else:
braintrust_token = os.getenv("BRAINTRUST_API_KEY")
if not braintrust_token:
raise HTTPException(
status_code=401,
detail="No Braintrust API token provided. Pass via Authorization header or set BRAINTRUST_API_KEY environment variable.",
)
# Call Braintrust API
braintrust_url = f"https://api.braintrust.dev/v1/prompt/{prompt_id}"
headers = {
"Authorization": f"Bearer {braintrust_token}",
"Accept": "application/json",
}
print(f"headers: {headers}")
print(f"braintrust_url: {braintrust_url}")
print(f"braintrust_token: {braintrust_token}")
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(braintrust_url, headers=headers)
response.raise_for_status()View on GitHub (pinned to 6c2dcb801b)
Solutions
- Make the custom config's transform_request_image_variation return a non-empty "data" payload, e.g. {"data": {"image": image, **optional_params}}.
- If using the stock OpenAI path, remove any monkeypatch of litellm.OpenAIImageVariationConfig so the built-in transform (which always includes the image under "data") is used.
- Pin or upgrade litellm to one consistent version so the handler's expectation of the "data" key matches the installed config implementation: pip install -U litellm.
- Route multipart/providers like Topaz through their own handler (model="topaz/...") instead of the OpenAI images.create_variation path, since their transforms return files/data httpx fields, not an OpenAI kwargs dict.
Example fix
# before (custom config returns no "data")
class MyConfig(OpenAIImageVariationConfig):
def transform_request_image_variation(self, model, image, optional_params, headers):
return {"image": image, **optional_params} # no "data" key -> ValueError
# after
class MyConfig(OpenAIImageVariationConfig):
def transform_request_image_variation(self, model, image, optional_params, headers):
return {"data": {"image": image, **optional_params}} Defensive patterns
Strategy: try-catch
Validate before calling
from litellm.types.utils import LlmProviders
from litellm.utils import ProviderConfigManager
config = ProviderConfigManager.get_provider_image_variation_config(
model="dall-e-2", provider=LlmProviders.OPENAI
)
assert config is not None, "no image-variation config for openai"
fields = config.transform_request_image_variation(
model="dall-e-2",
image=open("cat.png", "rb"),
optional_params={"n": 1, "size": "1024x1024"},
headers={},
)
if not fields.get("data"):
raise RuntimeError(
f"transform returned no 'data' payload; got keys={list(fields)} — fix the config"
) Type guard
from typing import Any
def has_variation_request_data(fields: Any) -> bool:
"""True if transform_request_image_variation returned a usable 'data' payload."""
return isinstance(fields, dict) and isinstance(fields.get("data"), dict) and len(fields["data"]) > 0 Try / catch
from litellm.llms.openai.common_utils import OpenAIError
try:
resp = litellm.image_variation(model="dall-e-2", image=img)
except (OpenAIError, ValueError) as e:
msg = str(e)
if "data field is required" in msg:
# internal transform-shape mismatch: retrying cannot help; inspect/fix the
# active ImageVariationConfig (custom monkeypatch or version drift)
raise RuntimeError(
"image-variation request transform returned no 'data'; "
"check custom ImageVariationConfig or litellm version"
) from e
raise Prevention
- When subclassing an image-variation config, always return {"data": {...}} with at least the image argument inside; add a unit test asserting fields["data"] is non-empty.
- Keep multipart providers (topaz) on their own routing path; their transforms return httpx files/data fields that the OpenAI create_variation handler will reject.
- Run transform_request_image_variation once in a smoke test at startup so shape mismatches fail before production traffic.
- Install litellm as a single pinned version (pip install litellm==X.Y.Z) — this error is a classic symptom of a handler/config version split.
When it happens
Trigger: A monkeypatched/replaced litellm.OpenAIImageVariationConfig whose transform_request_image_variation returns a mapping without a "data" key or with an empty one — e.g. a Topaz-style config returning HttpHandlerRequestFields(files={"image": ...}, data=optional_params) when optional_params is empty (data={} is falsy); mixing litellm versions where the config return shape (files/data fields) no longer matches what this handler unpacks.
Common situations: Custom image-variation provider configs built by subclassing BaseImageVariationConfig but forgetting to populate the "data" field; copying the Topaz multipart transform into an OpenAI-routed call; partial upgrades where a newer config class is loaded by an older handler.
Related errors
- File not found. banned_keywords_list={banned_keywords_list}
- Unclassified keys in {PRICES_PATH.name}: {', '.join(unclassi
- Braintrust API error: {e.response.text}
- Failed to connect to Braintrust API: {str(e)}
- Failed to parse Braintrust API response: {str(e)}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/081c0ea1ccd82bd3.
Report an issue: GitHub.