hiyouga/LlamaFactory · error · HTTPException
File access is restricted to the safe media directory.
Error message
File access is restricted to the safe media directory.
What it means
Raised as HTTP 403 by check_lfi_path when local files ARE allowed but os.path.realpath(path) does not fall under the realpath of SAFE_MEDIA_PATH. Even with ALLOW_LOCAL_FILES=true, media access is sandboxed to a single designated directory, blocking ../../ escapes and absolute paths elsewhere on disk.
Source
Thrown at src/llamafactory/api/common.py:63
def jsonify(data: "BaseModel") -> str:
try: # pydantic v2
return json.dumps(data.model_dump(exclude_unset=True), ensure_ascii=False)
except AttributeError: # pydantic v1
return data.json(exclude_unset=True, ensure_ascii=False)
def check_lfi_path(path: str) -> None:
"""Checks if a given path is vulnerable to LFI. Raises HTTPException if unsafe."""
if not ALLOW_LOCAL_FILES:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Local file access is disabled.")
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)View on GitHub (pinned to f28afaf635)
Solutions
- Copy or move the media files under the configured SAFE_MEDIA_PATH directory.
- Set SAFE_MEDIA_PATH (env/flag at startup) to the directory that already contains your media.
- Do not rely on ../ traversal or symlinks — realpath() canonicalizes both.
- Prefer data: URLs or an HTTP file server for one-off files.
Example fix
# before SAFE_MEDIA_PATH=/data/safe ... # request path: /datasets/img.png -> 403 # after (either copy media or repoint) cp /datasets/img.png /data/safe/ # then request /data/safe/img.png
Defensive patterns
Strategy: validation
Validate before calling
import os
def inside_safe_media(path, safe_root):
return os.path.realpath(path).startswith(os.path.realpath(safe_root) + os.sep)
assert inside_safe_media(media_path, SAFE_MEDIA_PATH) Type guard
const insideSafe = (p, root) => path.resolve(p).startsWith(path.resolve(root) + path.sep);
Try / catch
catch (e) { if (e.status === 403 && e.detail.includes('safe media directory')) { copyIntoSafeDir(mediaPath); retry; } else throw e; } Prevention
- Stage all request media into SAFE_MEDIA_PATH as part of payload preparation.
- Never use ../ relative traversal or symlinks — realpath() defeats both.
- Automate a preflight check of every media path against the safe root.
When it happens
Trigger: Local file enabled, but the path is /etc/passwd, /tmp/img.png, or any path whose canonicalized location is outside SAFE_MEDIA_PATH; symlinks inside the safe dir pointing outside (realpath resolves them, so they are rejected too).
Common situations: Enabling local files expecting the old unrestricted behavior; media stored next to the dataset rather than copied into the safe directory; symlinked model datasets.
Related errors
- Local file access is disabled.
- Invalid or inaccessible file path.
- Access to private or reserved IP addresses is not allowed.
- Please upgrade `transformers` to 4.34.0
- Unable to process key {key}
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/8becf8aabdfb1d21.
Report an issue: GitHub.