langflow-ai/langflow · warning · HTTPException
Profile picture not found
Error message
Profile picture not found
What it means
HTTP 404 from GET /files/profile_pictures/{folder_name}/{file_name} when the realpath-canonicalized candidate escapes the allowed base directory (config_dir/profile_pictures or the package's bundled profile_pictures). Both the config-dir and package-fallback branches use os.path.realpath + startswith checks; a candidate outside the base is reported as 'not found' (not 'forbidden') to avoid leaking path internals. Genuine missing files fall through to later checks, but this specific raise is the path-traversal guard.
Source
Thrown at src/backend/base/langflow/api/v1/files.py:228
raise HTTPException(status_code=400, detail=f"Folder must be one of: {', '.join(sorted(allowed_folders))}")
# SECURITY: Extract only the final path component to prevent path traversal.
# This is defense-in-depth on top of ValidatedFileName/ValidatedFolderName.
safe_folder = Path(folder_name).name
safe_file = Path(file_name).name
extension = safe_file.split(".")[-1]
config_dir = settings_service.settings.config_dir
# SECURITY: use os.path.realpath + startswith — the sanitiser pattern
# recognised by CodeQL's py/path-injection analysis. realpath canonicalises
# the path and resolves symlinks, so the subsequent startswith check is
# robust against both traversal sequences and symlink-based escapes.
# os.path.join is deliberate here (PTH118) to match CodeQL's sanitiser model.
allowed_base = os.path.realpath(os.path.join(str(config_dir), "profile_pictures")) # noqa: PTH118
candidate = os.path.realpath(os.path.join(allowed_base, safe_folder, safe_file)) # noqa: PTH118
if candidate != allowed_base and not candidate.startswith(allowed_base + os.sep):
raise HTTPException(status_code=404, detail="Profile picture not found")
file_path = Path(candidate)
# Fallback to package bundled profile pictures if not found in config_dir
if not file_path.exists():
from langflow.initial_setup import setup
package_base = os.path.realpath(str(Path(setup.__file__).parent / "profile_pictures"))
package_candidate = os.path.realpath(os.path.join(package_base, safe_folder, safe_file)) # noqa: PTH118
if package_candidate != package_base and not package_candidate.startswith(package_base + os.sep):
raise HTTPException(status_code=404, detail="Profile picture not found")
package_path = Path(package_candidate)
if package_path.exists():
file_path = package_path
else:
raise HTTPException(status_code=404, detail=f"Profile picture {safe_folder}/{safe_file} not found")
content_type = build_content_type_from_extension(extension)View on GitHub (pinned to 976ec789d2)
Solutions
- Request a plain file name that actually lives under profile_pictures/<folder>/ — no path separators or traversal
- Remove/repoint symlinks inside profile_pictures so all targets stay within the directory
- Re-check the folder allow-list first; valid folder + plain filename avoids this branch
Example fix
# before GET /files/profile_pictures/defaults/..%2F..%2Fsecret.png # 404 guard # after GET /files/profile_pictures/defaults/space.png
Defensive patterns
Strategy: validation
Validate before calling
import re
def is_safe_picture_name(file_name: str) -> bool:
# single path component, no traversal, no separators
return bool(re.fullmatch(r"[A-Za-z0-9._-]+", file_name)) and ".." not in file_name Type guard
function isSafePictureName(fileName: string): boolean {
return /^[A-Za-z0-9._-]+$/.test(fileName) && !fileName.includes("..");
} Try / catch
try:
client.get(f"/files/profile_pictures/{folder}/{name}").raise_for_status()
except HTTPError as e:
if e.response.status_code == 404:
use_default_picture() # treat as missing; never probe alternate paths
raise Prevention
- Never construct picture paths from user input without sanitization
- Keep symlinks out of profile_pictures directories
- Treat 404 uniformly (missing or blocked) and fall back to a bundled default
When it happens
Trigger: file_name containing traversal sequences that survive sanitization into the realpath check (e.g. names whose canonicalization lands outside the base); symlinked files pointing outside profile_pictures/; requesting ../../etc/passwd-style names via the route.
Common situations: Probing URLs with ../ sequences; symlinks in the profile pictures dir pointing elsewhere on disk; security scanners hitting the endpoint.
Related errors
- Not found
- This endpoint is not available
- Invalid path
- Invalid flow filename
- Invalid flow filename: '{flow_filename}'
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/0f587e6517c1df79.
Report an issue: GitHub.