Comfy-Org/ComfyUI · error · ValueError
destination escapes base directory
Error message
destination escapes base directory
What it means
Raised by validate_path_within_base when the absolute candidate path is not located under the absolute base directory (Path.is_relative_to check). It is the final containment guard on upload destinations: after joining a hashed basename onto the resolved base dir, the result must still live inside that base, blocking path-traversal writes (e.g. via '..' segments or crafted filenames).
Source
Thrown at app/assets/services/path_utils.py:72
bases = model_folder_paths[folder_name]
except KeyError:
raise ValueError(f"unknown model category '{folder_name}'")
if not bases:
raise ValueError(f"no base path configured for category '{folder_name}'")
base_dir = os.path.abspath(bases[0])
elif root == "input":
base_dir = os.path.abspath(folder_paths.get_input_directory())
else:
base_dir = os.path.abspath(folder_paths.get_output_directory())
return base_dir, []
def validate_path_within_base(candidate: str, base: str) -> None:
cand_abs = Path(os.path.abspath(candidate))
base_abs = Path(os.path.abspath(base))
if not cand_abs.is_relative_to(base_abs):
raise ValueError("destination escapes base directory")
def _compute_relative_path(child: str, parent: str) -> str:
rel = os.path.relpath(os.path.abspath(child), os.path.abspath(parent))
if rel == ".":
return ""
return rel.replace(os.sep, "/")
def _is_relative_to(child: str, parent: str) -> bool:
return Path(os.path.abspath(child)).is_relative_to(os.path.abspath(parent))
def compute_asset_response_paths(file_path: str) -> tuple[str, str | None] | None:
"""Return (logical_path, display_name) for a file path.
``logical_path`` is the internal namespaced storage locator (e.g.
``models/checkpoints/foo/bar.safetensors``); ``display_name`` is theView on GitHub (pinned to 1c6d8d45b3)
Solutions
- If you're a legitimate caller: don't craft extensions/subdirs — send a plain filename and let the server derive the extension; the hashed basename makes traversal impossible in normal flow.
- If you operate the server: ensure the input/output/model base directories are real directories, not symlinks whose targets confuse abspath containment checks.
- For security reviewers: this guard is the last line — also check the earlier extension length cap (<=16 chars) that constrains the join input.
Defensive patterns
Strategy: validation
Validate before calling
# Server-side: constrain extensions to a safe charset before the join
import re
SAFE_EXT = re.compile(r'^\.[A-Za-z0-9_-]{1,15}$')
def safe_ext(filename: str) -> str:
ext = os.path.splitext(os.path.basename(filename))[1]
return ext if SAFE_EXT.match(ext) else '' Type guard
def destination_is_within(dest: str, base: str) -> bool:
return Path(os.path.abspath(dest)).is_relative_to(Path(os.path.abspath(base))) Try / catch
try:
validate_path_within_base(dest_abs, base_dir)
except ValueError:
raise UploadRejected('invalid destination') # do not write bytes Prevention
- Always basename() client filenames before deriving extensions
- Reject extensions containing separators, '..', or unusual characters at the boundary
- Prefer hashed basenames (as ingest does) so user input never names the file directly
- Test the upload path with traversal payloads in CI
When it happens
Trigger: An upload whose client_filename/extension or tag-derived subdir produces a dest_abs outside base_dir — e.g. an extension containing path separators or '..' so os.path.join escapes the base; or a symlinked base where abspath resolution diverges. Called from ingest before any bytes are moved.
Common situations: Malicious or malformed filenames (ext like '/../../evil', sneaky unicode separators); mismatched path normalization between the tag resolution and the join; symlinked model/input directories making absolute-path comparison fail; security testing of the upload endpoint.
Related errors
- Invalid file path: {!r}
- INVALID_BODY
- INVALID_BODY
- System User prefix not allowed
- Video duration ({actual_duration:.2f}s) exceeds the maximum
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/2d82f3aae3222ebd.
Report an issue: GitHub.