BerriAI/litellm · error · OpenRouterException

Error parsing OpenRouter response: {e}

Error message

Error parsing OpenRouter response: {e}

What it means

LiteLLM's OpenRouter image-edit handler calls raw_response.json() on the HTTP response before mapping it to an ImageResponse. If the body is not valid JSON (empty body, HTML error page, plain-text gateway error), the parser's exception is wrapped in an OpenRouterException that forwards the real HTTP status code and headers. The underlying json() message is embedded in the exception text.

Source

Thrown at litellm/llms/openrouter/image_edit/transformation.py:205

        # Add mapped optional params (image_config, n, etc.)
        for key, value in image_edit_optional_request_params.items():
            if key not in ("model", "messages", "modalities"):
                request_body[key] = value

        empty_files: Final = cast(RequestFiles, [])
        return request_body, empty_files

    def transform_image_edit_response(
        self,
        model: str,
        raw_response: httpx.Response,
        logging_obj: LiteLLMLoggingObj,
    ) -> ImageResponse:
        try:
            response_json: Final = raw_response.json()
        except Exception as e:
            raise OpenRouterException(
                message=f"Error parsing OpenRouter response: {e}",
                status_code=raw_response.status_code,
                headers=raw_response.headers,
            )

        model_response: Final = ImageResponse()
        model_response.data = []

        try:
            choices: Final = response_json.get("choices", [])

            for choice in choices:
                message = choice.get("message", {})
                images = message.get("images", [])

                for image_data in images:
                    image_url_obj = image_data.get("image_url", {})
                    image_url = image_url_obj.get("url")

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Catch OpenRouterException and log status_code and headers; reproduce the same request with curl to see the raw body
  2. Verify the model actually supports image editing on OpenRouter and that the model string is prefixed openrouter/
  3. Remove or correct any custom api_base override so requests hit OpenRouter directly
  4. Retry with exponential backoff for transient 5xx/gateway responses
  5. Upgrade litellm to the latest release in case the handler's error path improved

Example fix

// before
resp = litellm.image_edit(model="openrouter/google/gemini-2.5-flash-image-preview", image=f, prompt="add a hat")

// after
from litellm.exceptions import OpenRouterException
try:
    resp = litellm.image_edit(model="openrouter/google/gemini-2.5-flash-image-preview", image=f, prompt="add a hat")
except OpenRouterException as e:
    logger.error("image_edit failed status=%s headers=%s msg=%s", e.status_code, dict(e.headers), e.message)
    raise
Defensive patterns

Strategy: try-catch

Try / catch

Wrap litellm.image_edit() in try/except OpenRouterException. Branch on e.status_code: 4xx points to config/model problems, 5xx or a message starting with 'Error parsing' points to gateway/transient issues - log e.headers and the message (it embeds the json() failure) before re-raising or falling back to another image provider.

Prevention

When it happens

Trigger: Calling litellm.image_edit() with an openrouter/* model when OpenRouter (or a proxy in front of it) returns a non-JSON body: an HTML 502/504 page from a gateway, an empty body from a timed-out upstream, a plain-text rate-limit message, or a custom api_base pointing at a service that does not return JSON.

Common situations: OpenRouter incidents or rate limits that surface HTML/text error pages; overriding api_base with a misconfigured internal gateway; requesting a model on the image-edit endpoint that upstream providers refuse with a non-JSON error.

Related errors


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