BerriAI/litellm · error · OpenAIError

{error_text}

Error message

{error_text}

What it means

litellm's image variations handler wraps any exception raised during the synchronous image-variation request (dall-e-2 /images/variations style calls) into an OpenAIError, extracting status_code (default 500), message text, and headers from the original exception. It is the single failure boundary for the whole non-streaming variation call - covering request build, HTTP send, and upstream API errors.

Source

Thrown at litellm/llms/openai/image_variations/handler.py:110

                    status_code=200,
                    request=httpx.Request(method="GET", url="https://litellm.ai"),  # mock request object
                ),
                logging_obj=logging_obj,
                request_data=data,
                image=image,
                optional_params=optional_params,
                litellm_params=litellm_params,
                encoding=None,
                api_key=api_key,
            )
        except Exception as e:
            status_code: Final = getattr(e, "status_code", 500)
            error_headers = getattr(e, "headers", None)
            error_text: Final = getattr(e, "text", str(e))
            error_response: Final = getattr(e, "response", None)
            if error_headers is None and error_response:
                error_headers = getattr(error_response, "headers", None)
            raise OpenAIError(status_code=status_code, message=error_text, headers=error_headers)

    def image_variations(
        self,
        model_response: ImageResponse,
        api_key: str,
        api_base: str,
        model: str | None,
        image: FileTypes,
        timeout: float | None,
        custom_llm_provider: str,
        logging_obj: LiteLLMLoggingObj,
        optional_params: dict,
        litellm_params: dict,
        print_verbose: Callable | None = None,
        logger_fn=None,
        client=None,
        organization: str | None = None,
        headers: dict | None = None,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Check OpenAIError.status_code: 401 = fix api_key, 429 = quota/rate limit, 400 = invalid image/params, 5xx = retry later
  2. Verify the input image meets requirements (PNG, size limits) for the variations endpoint
  3. Confirm api_base targets an endpoint that actually implements /images/variations if using a proxy/custom base
  4. Retry with backoff on 429/5xx; the call is idempotent for variations

Example fix

# before
resp = litellm.image_variations(image=open("cat.png","rb"), model="dall-e-2")
# OpenAIError with masked cause

# after
from litellm.exceptions import OpenAIError

try:
    resp = litellm.image_variations(image=open("cat.png","rb"), model="dall-e-2")
except OpenAIError as e:
    if getattr(e, "status_code", 500) == 401:
        raise RuntimeError("bad OPENAI_API_KEY") from e
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import os
from pathlib import Path

# pre-flight checks for the variations call
def can_run_variation(image_path: str) -> bool:
    p = Path(image_path)
    if not p.exists() or p.stat().st_size == 0:
        return False
    with p.open("rb") as f:
        magic = f.read(8)
    return magic.startswith(b"\x89PNG\r\n\x1a\n")

assert can_run_variation("cat.png"), "need a non-empty PNG"
assert os.getenv("OPENAI_API_KEY"), "need credentials"

Try / catch

from litellm.exceptions import OpenAIError

try:
    resp = litellm.image_variations(image=open("cat.png", "rb"), model="dall-e-2")
except OpenAIError as e:
    code = getattr(e, "status_code", 500)
    if code in (429, 500, 502, 503):
        time.sleep(2)  # transient - retry
        resp = litellm.image_variations(image=open("cat.png", "rb"), model="dall-e-2")
    elif code == 401:
        raise RuntimeError("invalid OPENAI_API_KEY") from e
    else:
        raise

Prevention

When it happens

Trigger: Calling litellm.image_variations() when anything fails: invalid/expired API key (401), insufficient quota (429/402), invalid image payload, upstream 5xx, network timeouts, or a bad custom api_base. The original error's status and text are re-raised inside OpenAIError.

Common situations: Using dall-e-2 variation calls with rotated keys or exhausted credits; passing non-PNG/oversized source images; custom OpenAI-compatible endpoints that do not implement /images/variations; transient network failures surfacing with status 500 because the wrapped exception lacked a status_code.

Related errors


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