hiyouga/LlamaFactory · error · HTTPException
Only HTTP/HTTPS URLs are allowed.
Error message
Only HTTP/HTTPS URLs are allowed.
What it means
Raised as HTTP 400 by check_ssrf_url when a media URL's scheme is not http or https. The SSRF guard first validates the scheme; data: URLs that reach this branch (they normally should not — base64 is matched earlier), ftp://, file://, gopher:// etc. are rejected before any DNS resolution.
Source
Thrown at src/llamafactory/api/common.py:75
try:
os.makedirs(SAFE_MEDIA_PATH, exist_ok=True)
real_path = os.path.realpath(path)
safe_path = os.path.realpath(SAFE_MEDIA_PATH)
if not real_path.startswith(safe_path):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="File access is restricted to the safe media directory."
)
except Exception:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid or inaccessible file path.")
def check_ssrf_url(url: str) -> None:
"""Checks if a given URL is vulnerable to SSRF. Raises HTTPException if unsafe."""
try:
parsed_url = urlparse(url)
if parsed_url.scheme not in ["http", "https"]:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Only HTTP/HTTPS URLs are allowed.")
hostname = parsed_url.hostname
if not hostname:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid URL hostname.")
ip_info = socket.getaddrinfo(hostname, parsed_url.port)
ip_address_str = ip_info[0][4][0]
ip = ipaddress.ip_address(ip_address_str)
if not ip.is_global:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access to private or reserved IP addresses is not allowed.",
)
except socket.gaierror:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=f"Could not resolve hostname: {parsed_url.hostname}"View on GitHub (pinned to f28afaf635)
Solutions
- Use only http:// or https:// URLs for remote media.
- For base64 payloads use the proper data:<mime>;base64,<data> form handled before this check.
- For local files use a filesystem path with ALLOW_LOCAL_FILES=true, not file://.
- Fix scheme typos (missing colon after http).
Example fix
// before
{type:'image_url', image_url:{url:'file:///data/img.png'}}
// after
ALLOW_LOCAL_FILES=true and url:'/safe-media/img.png' // local path
// or url:'https://cdn.example.com/img.png' Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def url_scheme_ok(u):
return urlparse(u).scheme in ("http", "https")
assert all(url_scheme_ok(u) for u in media_urls) Type guard
const schemeOk = (u) => ['http:', 'https:'].includes(new URL(u).protocol);
Try / catch
catch (e) { if (e.status === 400 && e.detail === 'Only HTTP/HTTPS URLs are allowed.') { u = toDataUrlOrHttps(u); retry; } throw e; } Prevention
- Reject file://, ftp://, and scheme-less URLs in the client before submission.
- Validate with URL parsing, not string prefixes, to catch http// typos.
- Keep base64 payloads in proper data:<mime>;base64, form.
When it happens
Trigger: image_url/video_url/audio_url set to ftp://host/img.png, file:///etc/passwd, or a mistyped scheme like http//example.com/a.png that urlparse yields an empty/odd scheme for.
Common situations: Supplying file:// URLs expecting local-file behavior (should use local paths with ALLOW_LOCAL_FILES instead); copy-paste scheme typos; protocols the guard intentionally does not fetch.
Related errors
- Invalid URL hostname.
- Invalid URL: {e}
- Could not resolve hostname: {parsed_url.hostname}
- Invalid or inaccessible file path.
- Access to private or reserved IP addresses is not allowed.
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/afcfa867f51771f4.
Report an issue: GitHub.