BerriAI/litellm · error · ValueError
Empty or unsafe filename
Error message
Empty or unsafe filename
What it means
After safe_filename() strips null bytes and removes all directory components (both '/' and '\' separators via replace + rsplit), it rejects a result that is empty, '.', or '..' with this ValueError. This stops uploads whose entire filename is a path reference — for example 'folder/' or '..' — from turning into a directory write. It is the last guard before the name is used to write a temp file.
Source
Thrown at litellm/proxy/common_utils/path_utils.py:63
Strips all directory components (both Unix and Windows separators),
returning only the final name. Use this for uploaded file names
before writing to disk.
Args:
filename: User-supplied filename (may contain path separators).
Returns:
The basename only, with no directory components.
Raises:
ValueError: If the resulting filename is empty or contains null bytes.
"""
if "\x00" in filename:
raise ValueError("Filename contains null byte")
# Normalize backslash separators for cross-platform safety
name: Final = filename.replace("\\", "/").rsplit("/", 1)[-1]
if not name or name in (".", ".."):
raise ValueError("Empty or unsafe filename")
return name
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Always set an explicit, non-empty basename with the expected extension, e.g. 'convert.prompt'.
- Validate the filename client-side: non-empty, no separators, not '.' or '..'.
- When the filename derives from user input, generate a server-side safe name (for example a slug or UUID plus extension).
Example fix
# before
files = {"file": ("../", content)}
requests.post(url, files=files)
# after
files = {"file": ("convert.prompt", content)}
requests.post(url, files=files) Defensive patterns
Strategy: validation
Validate before calling
def clean_upload_name(raw: str, fallback: str = "upload.prompt") -> str:
name = raw.replace("\\", "/").rsplit("/", 1)[-1].strip()
if not name or name in (".", ".."):
return fallback
return name
files = {"file": (clean_upload_name(user_filename), content)} Type guard
def is_non_trivial_basename(filename: str) -> bool:
name = filename.replace("\\", "/").rsplit("/", 1)[-1]
return bool(name) and name not in (".", "..") Try / catch
try:
resp = client.post(url, files={"file": (raw_name, content)})
resp.raise_for_status()
except HTTPError as e:
if "unsafe filename" in e.response.text.lower():
resp = client.post(url, files={"file": ("upload.prompt", content)})
raise Prevention
- Always set an explicit filename with a real basename and the expected extension in multipart uploads.
- Validate filenames from user input: non-empty, no separators, not '.' or '..'.
- Prefer server-generated names for stored uploads; keep the original name in metadata only.
When it happens
Trigger: Upload a file whose filename is '..', '.', 'some/dir/' (trailing slash leaves an empty basename), or an empty string. The multipart filename header fully controls this, so any such value triggers the error on upload endpoints that use safe_filename (prompt file conversion).
Common situations: Programmatic uploads where the filename is built from user input that happens to be empty or a path fragment. HTTP clients that default to odd filenames when none is given. Security tests probing upload handling.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Filename contains null byte
- extract_file_data does not accept bare str inputs. Pass byte
- Invalid identifier {identifier!r}: contains disallowed chara
- Invalid identifier {identifier!r}: path traversal detected
- Callback param '{param}' (from {source}) contains an 'os.env
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/6bfb66c27c148b41.
Report an issue: GitHub.