BerriAI/litellm · error · ValueError

Voyage API key is required for multimodal embeddings. Set VO

Error message

Voyage API key is required for multimodal embeddings. Set VOYAGE_API_KEY / VOYAGE_AI_API_KEY / VOYAGE_AI_TOKEN or pass `api_key` explicitly.

What it means

Voyage multimodal embeddings (VoyageMultimodalEmbeddingConfig.validate_environment) build an Authorization: Bearer header. The key is taken from the api_key argument or the env chain VOYAGE_API_KEY, VOYAGE_AI_API_KEY, VOYAGE_AI_TOKEN; if all are empty, this ValueError is raised before the request is sent. Multimodal embedding calls need this even when text-only embedding calls elsewhere in the app already work.

Source

Thrown at litellm/llms/voyage/embedding/transformation_multimodal.py:94

    def validate_environment(
        self,
        headers: dict,
        model: str,
        messages: list[AllMessageValues],
        optional_params: dict,
        litellm_params: dict,
        api_key: str | None = None,
        api_base: str | None = None,
    ) -> dict:
        if api_key is None:
            api_key = (
                get_secret_str("VOYAGE_API_KEY")
                or get_secret_str("VOYAGE_AI_API_KEY")
                or get_secret_str("VOYAGE_AI_TOKEN")
            )
        if not api_key:
            raise ValueError(
                "Voyage API key is required for multimodal embeddings. "
                "Set VOYAGE_API_KEY / VOYAGE_AI_API_KEY / VOYAGE_AI_TOKEN "
                "or pass `api_key` explicitly."
            )
        return {"Authorization": f"Bearer {api_key}"}

    def _normalize_content_item(self, item: dict[str, Any]) -> dict[str, Any]:
        item_type: Final = item.get("type")
        if item_type == "image_url":
            image_url = item.get("image_url")
            if isinstance(image_url, dict):
                image_url = image_url.get("url")
            if image_url is None:
                raise ValueError(
                    "Voyage multimodal embeddings require a non-empty `image_url`. "
                    "Got an image content block without a `url`."
                )
            if isinstance(image_url, str) and image_url.startswith("data:image/"):

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Export VOYAGE_API_KEY (or VOYAGE_AI_API_KEY / VOYAGE_AI_TOKEN) in the runtime environment.
  2. Or pass api_key directly to the embedding call.
  3. Store the secret in the platform's secret manager and inject it as VOYAGE_API_KEY at startup.
  4. Add a startup assertion: assert os.environ.get('VOYAGE_API_KEY'), 'VOYAGE_API_KEY missing'.

Example fix

# before
resp = litellm.embedding(
    model="voyage-3-multimodal",
    input=[{"content": [{"type": "text", "text": "cat"}, {"type": "image_url", "image_url": "https://..."}]}],
)
# -> ValueError: Voyage API key is required for multimodal embeddings...

# after
resp = litellm.embedding(
    model="voyage-3-multimodal",
    input=[{"content": [{"type": "text", "text": "cat"}, {"type": "image_url", "image_url": "https://..."}]}],
    api_key=os.environ["VOYAGE_API_KEY"],
)
Defensive patterns

Strategy: validation

Validate before calling

import os

VOYAGE_KEY = (
    os.getenv("VOYAGE_API_KEY")
    or os.getenv("VOYAGE_AI_API_KEY")
    or os.getenv("VOYAGE_AI_TOKEN")
)
if not VOYAGE_KEY:
    raise RuntimeError("Voyage multimodal embedding requires VOYAGE_API_KEY")
resp = litellm.embedding(model="voyage-3-multimodal", input=inputs, api_key=VOYAGE_KEY)

Type guard

const hasVoyageKey = (env: Record<string, string | undefined>): boolean =>
  Boolean(env.VOYAGE_API_KEY ?? env.VOYAGE_AI_API_KEY ?? env.VOYAGE_AI_TOKEN);

Try / catch

try:
    resp = litellm.embedding(model="voyage-3-multimodal", input=inputs)
except ValueError as e:
    if "Voyage API key is required" in str(e):
        raise RuntimeError("Set VOYAGE_API_KEY for multimodal embeddings") from e
    raise

Prevention

When it happens

Trigger: litellm.embedding(model="voyage-3-multimodal", input=[{"content": [...]}]) with no api_key and none of the three Voyage env vars set; using the older VOYAGE_AI_TOKEN name after an env cleanup; serverless functions that do not inherit local shell env.

Common situations: Adding image embedding to an existing text pipeline where the key was only configured for a different provider; typos in env var names; deploying to Vercel/Lambda without migrating the secret.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/d377abf4bdc0b1e4. Report an issue: GitHub.