xtekky/gpt4free · error · ValueError
Invalid or unsafe image url: {image}
Error message
Invalid or unsafe image url: {image} What it means
Thrown inside copy_images.py's copy_image() when an http(s) download is required and is_safe_url(image) returns False. is_safe_url (g4f/image/__init__.py:125) is an SSRF guard: it only allows http/https, rejects backslashes, missing hostnames, and URLs whose hostname resolves to private/loopback/reserved addresses (also cross-checking via urllib3 to catch rebinding tricks). This is a deliberate security block, not a bug.
Source
Thrown at g4f/image/copy_images.py:198
media_extension = get_media_extension(image)
path = urlparse(image).path
if path.startswith("/media/"):
filename = secure_filename(path[len("/media/") :])
else:
filename = get_filename(tags, alt, media_extension, image)
target_path = os.path.join(dest_dir, filename)
try:
if image.startswith("data:"):
with open(target_path, "wb") as f:
f.write(extract_data_uri(image))
elif (
not os.path.exists(target_path)
or os.lstat(target_path).st_size <= 0
):
if not is_safe_url(image):
raise ValueError(f"Invalid or unsafe image url: {image}")
async with session.get(image, ssl=ssl) as response:
response.raise_for_status()
if target is None:
filename = update_filename(response, filename)
target_path = os.path.join(dest_dir, filename)
media_type = response.headers.get(
"content-type", "application/octet-stream"
)
if media_type not in (
"application/octet-stream",
"binary/octet-stream",
):
if media_type not in MEDIA_TYPE_MAP:
raise ValueError(
f"Unsupported media type: {media_type}"
)
if target is None and not media_extension:
media_extension = f".{MEDIA_TYPE_MAP[media_type]}"View on GitHub (pinned to 973504e177)
Solutions
- Serve the media from a public http(s) host whose DNS resolves to a public IP.
- For localhost testing, fetch the bytes yourself and pass a data: URI (copy_image handles 'data:' without the URL check) or place the file directly in the media dir.
- Do not attempt to bypass the guard in production — it exists to stop SSRF; if an internal host is genuinely required, extend is_safe_url deliberately with an allowlist.
Example fix
// before
images = await copy_images(["http://192.168.1.10:8080/pic.png"], ...)
// after
import base64
data_uri = "data:image/png;base64," + base64.b64encode(open('pic.png','rb').read()).decode()
images = await copy_images([data_uri], ...) Defensive patterns
Strategy: validation
Validate before calling
from g4f.image import is_safe_url
def downloadable(image_url: str) -> bool:
return (
image_url.startswith("data:")
or image_url.startswith("/")
or is_safe_url(image_url)
) Try / catch
try:
await copy_images([url], prompt=prompt, tags=tags)
except ValueError as e:
if "unsafe image url" in str(e):
logger.warning("blocked internal/unsafe url: %s", url)
return [] # skip, do not bypass
raise Prevention
- Treat this error as an SSRF guard firing correctly — fix the URL, never the check.
- For local assets, embed them as data: URIs or write them into the media dir directly.
- Resolve/validate hostnames against public IPs before handing URLs to copy_images in server contexts.
When it happens
Trigger: Passing URLs like http://127.0.0.1/x.png, http://10.0.0.5/a.jpg, http://[::1]/f.webp, http://169.254.169.254/latest/meta-data, a hostname that DNS-resolves to an internal IP, or a non-http scheme such as ftp:// or file://.
Common situations: Local development pointing at localhost media servers (blocked by design); cloud metadata endpoint attempts; hostnames that resolve differently inside the deployment network (internal DNS to 192.168.x.x); URLs containing encoded backslashes.
Related errors
- CDP module is required for Cloudflare provider. Please ensur
- WebSocket Error: {ws.exception()}
- Yupp request failed: {str(e)}
- Token refresh failed: {text}
- All Antigravity endpoints failed. Last error: {last_error}
AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14).
Data as JSON: /api/errors/1f20ddb6e76dcc22.
Report an issue: GitHub.