HKUDS/Vibe-Trading · error · ValueError

unsafe media URL: {error}

Error message

unsafe media URL: {error}

What it means

Raised by TelegramChannel.send when media_path is a remote HTTP(S) URL that fails the SSRF safety check validate_url_target. Before passing a URL straight to Telegram's sendPhoto/sendVideo/etc., the channel validates the target to block requests to private/internal networks, so attackers cannot use the bot to probe intranet endpoints.

Source

Thrown at agent/src/channels/telegram.py:791

                    "video": self._app.bot.send_video,
                    "voice": self._app.bot.send_voice,
                    "audio": self._app.bot.send_audio,
                }.get(media_type, self._app.bot.send_document)
                param = {
                    "photo": "photo",
                    "video": "video",
                    "voice": "voice",
                    "audio": "audio",
                }.get(media_type, "document")
                extra: dict[str, Any] = {}
                if media_type == "video":
                    extra["supports_streaming"] = True

                # Telegram Bot API accepts HTTP(S) URLs directly for media params.
                if self._is_remote_media_url(media_path):
                    ok, error = validate_url_target(media_path)
                    if not ok:
                        raise ValueError(f"unsafe media URL: {error}")
                    await self._call_with_retry(
                        sender,
                        chat_id=chat_id,
                        **{param: media_path},
                        reply_parameters=reply_params,
                        **thread_kwargs,
                        **extra,
                    )
                    continue

                media_bytes = Path(media_path).read_bytes()
                filename = Path(media_path).name
                send_kwargs = {param: media_bytes, "filename": filename}
                await self._call_with_retry(
                    sender,
                    chat_id=chat_id,
                    reply_parameters=reply_params,
                    **thread_kwargs,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Serve media from a genuinely public HTTPS host that resolves to a public IP
  2. For internal media, download the bytes yourself (from inside your trusted network) and pass a local file path or upload bytes instead of a URL
  3. Add an explicit allowlist/egress proxy for trusted internal media hosts and route through it rather than passing raw internal URLs
  4. Check DNS resolution in the runtime environment — a hostname that is public in CI may resolve privately in-cluster

Example fix

# before
await channel.send("chat", text="hi", media_path="http://10.0.0.5/media/cat.jpg")
# after
async with httpx.AsyncClient() as c:  # fetch internally, then send local file
    r = await c.get("http://10.0.0.5/media/cat.jpg")
    p = Path("/tmp/cat.jpg"); p.write_bytes(r.content)
await channel.send("chat", text="hi", media_path=str(p))
Defensive patterns

Strategy: fallback

Validate before calling

from agent.src.utils.url_safety import validate_url_target  # same helper the channel uses

async def safe_media(url: str) -> str | None:
    ok, _err = validate_url_target(url)
    return url if ok else None

url = await safe_media(media_path)
if url is None:
    # pre-download and send local file instead
    ...

Type guard

def is_public_media_url(u: str) -> bool:
    ok, _ = validate_url_target(u)
    return ok

Try / catch

try:
    await channel.send(chat, text=msg, media_path=media_url)
except ValueError as e:
    if "unsafe media URL" in str(e):
        data = await fetch_bytes_locally(media_url)  # trusted internal fetch
        await channel.send(chat, text=msg, media_path=await save_tmp(data))
    else:
        raise

Prevention

When it happens

Trigger: Calling send() with media_path like "http://169.254.169.254/latest/meta-data", "http://localhost:8080/x.png", "https://10.0.0.5/file.jpg", or a URL whose host resolves to a private IP; validate_url_target returns (False, reason) and the send aborts.

Common situations: Media served from an internal service (minio on a LAN IP, k8s service DNS, docker network hostnames); running the agent in a container where a public-looking hostname resolves internally; localhost URLs during local testing; cloud metadata endpoints accidentally used as media sources.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/8620804bded8460e. Report an issue: GitHub.