[Bug fix] Misclassified 500 error on invalid image_url in /chat/completions request (#14149)

* add ImageFetchError

* docs ImageFetchError

* fix ImageFetchError

* test_completion_with_invalid_image_url
This commit is contained in:
Ishaan Jaff 2025-09-01 15:26:27 -07:00 • committed by GitHub
parent 2985915357
commit 58b713d19d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 5 deletions

View file

@ -12,6 +12,7 @@ All exceptions can be imported from `litellm` - e.g. `from litellm import BadReq
| 400 | UnsupportedParamsError | litellm.BadRequestError | Raised when unsupported params are passed |
| 400 | ContextWindowExceededError| litellm.BadRequestError | Special error type for context window exceeded error messages - enables context window fallbacks |
| 400 | ContentPolicyViolationError| litellm.BadRequestError | Special error type for content policy violation error messages - enables content policy fallbacks |
| 400 | ImageFetchError | litellm.BadRequestError | Raised when there are errors fetching or processing images |
| 400 | InvalidRequestError | openai.BadRequestError | Deprecated error, use BadRequestError instead |
| 401 | AuthenticationError | openai.AuthenticationError |
| 403 | PermissionDeniedError | openai.PermissionDeniedError |

View file

@ -1261,6 +1261,7 @@ from .exceptions import (
AuthenticationError,
InvalidRequestError,
BadRequestError,
ImageFetchError,
NotFoundError,
RateLimitError,
ServiceUnavailableError,

View file

@ -153,6 +153,29 @@ class BadRequestError(openai.BadRequestError): # type: ignore
_message += f", LiteLLM Max Retries: {self.max_retries}"
return _message
class ImageFetchError(BadRequestError):
def __init__(
self,
message,
model=None,
llm_provider=None,
response: Optional[httpx.Response] = None,
litellm_debug_info: Optional[str] = None,
max_retries: Optional[int] = None,
num_retries: Optional[int] = None,
body: Optional[dict] = None,
):
super().__init__(
message=message,
model=model,
llm_provider=llm_provider,
response=response,
litellm_debug_info=litellm_debug_info,
max_retries=max_retries,
num_retries=num_retries,
body=body,
)
class UnprocessableEntityError(openai.UnprocessableEntityError): # type: ignore
def __init__(

View file

@ -17,7 +17,7 @@ in_memory_cache = InMemoryCache(max_size_in_memory=MAX_IMGS_IN_MEMORY)
def _process_image_response(response: Response, url: str) -> str:
if response.status_code != 200:
raise Exception(
raise litellm.ImageFetchError(
f"Error: Unable to fetch image from URL. Status code: {response.status_code}, url={url}"
)
@ -57,9 +57,11 @@ async def async_convert_url_to_base64(url: str) -> str:
try:
response = await client.get(url, follow_redirects=True)
return _process_image_response(response, url)
except litellm.ImageFetchError:
raise
except Exception:
pass
raise Exception(
raise litellm.ImageFetchError(
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
)
@ -74,10 +76,11 @@ def convert_url_to_base64(url: str) -> str:
try:
response = client.get(url, follow_redirects=True)
return _process_image_response(response, url)
except litellm.ImageFetchError:
raise
except Exception as e:
verbose_logger.exception(e)
# print(e)
pass
raise Exception(
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
raise litellm.ImageFetchError(
f"Error: Unable to fetch image from URL after 3 attempts. url={url}",
)

View file

@ -0,0 +1,41 @@
import pytest
from httpx import Request, Response
import litellm
from litellm.litellm_core_utils.prompt_templates.image_handling import (
convert_url_to_base64,
)
class DummyClient:
def get(self, url, follow_redirects=True):
return Response(status_code=404, request=Request("GET", url))
def test_invalid_image_url_raises_bad_request(monkeypatch):
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
with pytest.raises(litellm.ImageFetchError) as excinfo:
convert_url_to_base64("https://invalid.example/image.png")
assert "Unable to fetch image" in str(excinfo.value)
def test_completion_with_invalid_image_url(monkeypatch):
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "hi"},
{
"type": "image_url",
"image_url": {"url": "https://invalid.example/image.png"},
},
],
}
]
with pytest.raises(litellm.ImageFetchError) as excinfo:
litellm.completion(
model="gemini/gemini-pro", messages=messages, api_key="test"
)
assert excinfo.value.status_code == 400
assert "Unable to fetch image" in str(excinfo.value)