Bug Description
vision_analyze retries non-retryable HTTP 4xx responses. When _download_image()
(tools/vision_tools.py) gets a 404/403, response.raise_for_status() raises an
httpx.HTTPStatusError that is caught by a broad except Exception, which then retries the
identical URL 3× with 2s/4s exponential backoff before failing. A 4xx for a fixed URL is
deterministic — the retries can never succeed, so they only add ~6s of latency and flood the
logs with duplicate ERROR tracebacks.
Observed in a gateway session (the model had constructed a bad Wikimedia Commons thumbnail
URL — wrong hash prefix + a non-existent thumb size):
WARNING tools.vision_tools: Image download failed (attempt 1/3): Client error '404 Not Found' ...
WARNING tools.vision_tools: Retrying in 2s...
WARNING tools.vision_tools: Image download failed (attempt 2/3): Client error '404 Not Found' ...
WARNING tools.vision_tools: Retrying in 4s...
ERROR tools.vision_tools: Image download failed after 3 attempts: Client error '404 Not Found' ...
(The model fabricating the URL is separate model behavior — this issue is only about the
download layer wasting retries on a response that can't change.)
Steps to Reproduce
- Call
vision_analyze with an image_url that returns 404 (any stable dead image URL).
- Watch the logs: 3 attempts with 2s + 4s sleeps between them.
Expected Behavior
Fail fast on non-retryable 4xx client errors (404, 403, etc.) — one request, no backoff.
429 Too Many Requests is the one 4xx worth retrying with backoff, so it should be preserved.
Actual Behavior
All 4xx are retried max_retries (3) times with exponential backoff, adding ~6s and
duplicate ERROR tracebacks before surfacing the same error.
Affected Component
Tools — tools/vision_tools.py::_download_image
Version
v2026.5.16
Proposed Fix
In the retry loop, re-raise immediately when the caught exception is an
httpx.HTTPStatusError with a 4xx status other than 429. PR to follow.
Bug Description
vision_analyzeretries non-retryable HTTP 4xx responses. When_download_image()(
tools/vision_tools.py) gets a404/403,response.raise_for_status()raises anhttpx.HTTPStatusErrorthat is caught by a broadexcept Exception, which then retries theidentical URL 3× with 2s/4s exponential backoff before failing. A 4xx for a fixed URL is
deterministic — the retries can never succeed, so they only add ~6s of latency and flood the
logs with duplicate ERROR tracebacks.
Observed in a gateway session (the model had constructed a bad Wikimedia Commons thumbnail
URL — wrong hash prefix + a non-existent thumb size):
(The model fabricating the URL is separate model behavior — this issue is only about the
download layer wasting retries on a response that can't change.)
Steps to Reproduce
vision_analyzewith animage_urlthat returns404(any stable dead image URL).Expected Behavior
Fail fast on non-retryable 4xx client errors (404, 403, etc.) — one request, no backoff.
429 Too Many Requestsis the one 4xx worth retrying with backoff, so it should be preserved.Actual Behavior
All 4xx are retried
max_retries(3) times with exponential backoff, adding ~6s andduplicate ERROR tracebacks before surfacing the same error.
Affected Component
Tools —
tools/vision_tools.py::_download_imageVersion
v2026.5.16
Proposed Fix
In the retry loop, re-raise immediately when the caught exception is an
httpx.HTTPStatusErrorwith a 4xx status other than 429. PR to follow.