Skip to content

Commit 58b713d

Browse files
authored
[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
1 parent 2985915 commit 58b713d

File tree

5 files changed

+74
-5
lines changed

5 files changed

+74
-5
lines changed

docs/my-website/docs/exception_mapping.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ All exceptions can be imported from `litellm` - e.g. `from litellm import BadReq
1212
| 400 | UnsupportedParamsError | litellm.BadRequestError | Raised when unsupported params are passed |
1313
| 400 | ContextWindowExceededError| litellm.BadRequestError | Special error type for context window exceeded error messages - enables context window fallbacks |
1414
| 400 | ContentPolicyViolationError| litellm.BadRequestError | Special error type for content policy violation error messages - enables content policy fallbacks |
15+
| 400 | ImageFetchError | litellm.BadRequestError | Raised when there are errors fetching or processing images |
1516
| 400 | InvalidRequestError | openai.BadRequestError | Deprecated error, use BadRequestError instead |
1617
| 401 | AuthenticationError | openai.AuthenticationError |
1718
| 403 | PermissionDeniedError | openai.PermissionDeniedError |

litellm/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,7 @@ def add_known_models():
12611261
AuthenticationError,
12621262
InvalidRequestError,
12631263
BadRequestError,
1264+
ImageFetchError,
12641265
NotFoundError,
12651266
RateLimitError,
12661267
ServiceUnavailableError,

litellm/exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,29 @@ def __repr__(self):
153153
_message += f", LiteLLM Max Retries: {self.max_retries}"
154154
return _message
155155

156+
class ImageFetchError(BadRequestError):
157+
def __init__(
158+
self,
159+
message,
160+
model=None,
161+
llm_provider=None,
162+
response: Optional[httpx.Response] = None,
163+
litellm_debug_info: Optional[str] = None,
164+
max_retries: Optional[int] = None,
165+
num_retries: Optional[int] = None,
166+
body: Optional[dict] = None,
167+
):
168+
super().__init__(
169+
message=message,
170+
model=model,
171+
llm_provider=llm_provider,
172+
response=response,
173+
litellm_debug_info=litellm_debug_info,
174+
max_retries=max_retries,
175+
num_retries=num_retries,
176+
body=body,
177+
)
178+
156179

157180
class UnprocessableEntityError(openai.UnprocessableEntityError): # type: ignore
158181
def __init__(

litellm/litellm_core_utils/prompt_templates/image_handling.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
def _process_image_response(response: Response, url: str) -> str:
1919
if response.status_code != 200:
20-
raise Exception(
20+
raise litellm.ImageFetchError(
2121
f"Error: Unable to fetch image from URL. Status code: {response.status_code}, url={url}"
2222
)
2323

@@ -57,9 +57,11 @@ async def async_convert_url_to_base64(url: str) -> str:
5757
try:
5858
response = await client.get(url, follow_redirects=True)
5959
return _process_image_response(response, url)
60+
except litellm.ImageFetchError:
61+
raise
6062
except Exception:
6163
pass
62-
raise Exception(
64+
raise litellm.ImageFetchError(
6365
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
6466
)
6567

@@ -74,10 +76,11 @@ def convert_url_to_base64(url: str) -> str:
7476
try:
7577
response = client.get(url, follow_redirects=True)
7678
return _process_image_response(response, url)
79+
except litellm.ImageFetchError:
80+
raise
7781
except Exception as e:
7882
verbose_logger.exception(e)
79-
# print(e)
8083
pass
81-
raise Exception(
82-
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
84+
raise litellm.ImageFetchError(
85+
f"Error: Unable to fetch image from URL after 3 attempts. url={url}",
8386
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pytest
2+
from httpx import Request, Response
3+
4+
import litellm
5+
from litellm.litellm_core_utils.prompt_templates.image_handling import (
6+
convert_url_to_base64,
7+
)
8+
9+
10+
class DummyClient:
11+
def get(self, url, follow_redirects=True):
12+
return Response(status_code=404, request=Request("GET", url))
13+
14+
15+
def test_invalid_image_url_raises_bad_request(monkeypatch):
16+
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
17+
with pytest.raises(litellm.ImageFetchError) as excinfo:
18+
convert_url_to_base64("https://invalid.example/image.png")
19+
assert "Unable to fetch image" in str(excinfo.value)
20+
21+
22+
def test_completion_with_invalid_image_url(monkeypatch):
23+
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
24+
messages = [
25+
{
26+
"role": "user",
27+
"content": [
28+
{"type": "text", "text": "hi"},
29+
{
30+
"type": "image_url",
31+
"image_url": {"url": "https://invalid.example/image.png"},
32+
},
33+
],
34+
}
35+
]
36+
with pytest.raises(litellm.ImageFetchError) as excinfo:
37+
litellm.completion(
38+
model="gemini/gemini-pro", messages=messages, api_key="test"
39+
)
40+
assert excinfo.value.status_code == 400
41+
assert "Unable to fetch image" in str(excinfo.value)

0 commit comments

Comments
 (0)