Duplicate check
AgentOS version or commit
2026.9.1
Area
Tools
Reproduction steps
Trigger an image fetch against an endpoint that returns a 3xx redirect status without a Location header (or an empty Location header):
python
import asyncio, httpx
from agentos.tools.builtin.media import _fetch_image_url
Mock server returning 302 with missing location header
class _MockTransport(httpx.AsyncBaseTransport):
async def handle_async_request(self, request):
return httpx.Response(302, headers={}, stream=httpx.ByteStream(b""))
Invoke await _fetch_image_url("http://example.com/test.png").
Expected behavior
The redirect loop should inspect the Location header before closing the response stream. If a 3xx response lacks a valid Location header, it should raise a descriptive ToolError indicating a malformed redirect missing the Location header.
Actual behavior
At lines 310–313 of src/agentos/tools/builtin/media.py:
python
location = resp.headers.get("location")
await resp.aclose()
if not location:
break
resp.aclose() is awaited before if not location: break. Breaking out of the loop then hits resp.raise_for_status() which fails with httpx.HTTPStatusError: Redirect response '302 Found' for url ..., or attempts async for chunk in resp.aiter_bytes(...) on a closed stream, surfacing as misleading Failed to fetch image from URL: Attempted to read or stream content, but the stream has been closed.
Environment
Windows 10 / Linux / macOS, Python 3.12+ (tested on Python 3.14.3 with uv)
Duplicate check
AgentOS version or commit
2026.9.1
Area
Tools
Reproduction steps
Trigger an image fetch against an endpoint that returns a 3xx redirect status without a Location header (or an empty Location header):
python
import asyncio, httpx
from agentos.tools.builtin.media import _fetch_image_url
Mock server returning 302 with missing location header
class _MockTransport(httpx.AsyncBaseTransport):
async def handle_async_request(self, request):
return httpx.Response(302, headers={}, stream=httpx.ByteStream(b""))
Invoke await _fetch_image_url("http://example.com/test.png").
Expected behavior
The redirect loop should inspect the Location header before closing the response stream. If a 3xx response lacks a valid Location header, it should raise a descriptive ToolError indicating a malformed redirect missing the Location header.
Actual behavior
At lines 310–313 of src/agentos/tools/builtin/media.py:
python
location = resp.headers.get("location")
await resp.aclose()
if not location:
break
resp.aclose() is awaited before if not location: break. Breaking out of the loop then hits resp.raise_for_status() which fails with httpx.HTTPStatusError: Redirect response '302 Found' for url ..., or attempts async for chunk in resp.aiter_bytes(...) on a closed stream, surfacing as misleading Failed to fetch image from URL: Attempted to read or stream content, but the stream has been closed.
Environment
Windows 10 / Linux / macOS, Python 3.12+ (tested on Python 3.14.3 with uv)