langflow-ai/langflow · error · HTTPException

Invalid path: resolves outside allowed directory

Error message

Invalid path: resolves outside allowed directory

What it means

HTTP 400: the (relative) fs_path resolved via realpath to a location outside <data_dir>/flows/<user_id> — i.e. it escapes the allowed directory even though it contained no '..'. This is the symlink-escape branch: a symlink inside the flows dir pointing elsewhere makes the canonicalised path fail the startswith containment check.

Source

Thrown at src/backend/base/langflow/api/v1/flows_helpers.py:105

        candidate = normalized_path
    else:
        relative_part = normalized_path.lstrip("/")
        # os.path.join is deliberate here (PTH118) to match CodeQL's sanitiser model.
        candidate = os.path.join(base_dir_resolved, relative_part) if relative_part else base_dir_resolved  # noqa: PTH118

    try:
        resolved_str = os.path.realpath(candidate)
    except (OSError, ValueError) as e:
        raise HTTPException(status_code=400, detail=f"Invalid path: {e}") from e

    # SECURITY: containment check using os.path.realpath + startswith (CodeQL-recognised).
    if resolved_str != base_dir_resolved and not resolved_str.startswith(base_dir_resolved + os.sep):
        if is_absolute:
            raise HTTPException(
                status_code=400,
                detail="Absolute path must be within your flows directory",
            )
        raise HTTPException(
            status_code=400,
            detail="Invalid path: resolves outside allowed directory",
        )

    # Return the canonicalised path — safe for subsequent filesystem operations.
    return Path(resolved_str)


# Fields that may be updated via setattr on a Flow ORM instance.
# Any key not in this set is silently dropped to prevent callers from
# overwriting internal fields (e.g. ``id``, ``user_id``).
_UPDATABLE_FLOW_FIELDS: frozenset[str] = frozenset(
    {
        "name",
        "description",
        "data",
        "is_component",
        "endpoint_name",

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Remove or fix symlinks under <data_dir>/flows/<user_id>/ so every entry is a real file/subdirectory.
  2. Copy the target file into the flows directory instead of symlinking it.
  3. Use a plain relative filename with no directory components to avoid touching any symlinks.

Example fix

# server-side, before: flows/<uid>/shared -> /srv/shared (symlink)
# after: real directory
cp -rL /srv/shared /data/flows/<uid>/shared  # dereference into a real dir
Defensive patterns

Strategy: validation

Validate before calling

find /data/flows/<user_id> -type l  # empty output means no symlink-escape risk

Prevention

When it happens

Trigger: A file or directory under flows/<user_id>/ is a symlink to somewhere outside (e.g. "shared" -> "/etc"), and fs_path goes through it; or a race where the path changed between join and realpath; also any relative path that textually stays inside but canonically lands outside.

Common situations: Admin or user created symlinks inside the storage dir to share files or save disk; container setups where the flows dir contains mount-point symlinks; attackers planting a symlink to escalate the write outside the sandbox.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/4846d78eb3791764. Report an issue: GitHub.