langflow-ai/langflow · warning · HTTPException

fs_path cannot be empty

Error message

fs_path cannot be empty

What it means

HTTP 400 from _get_safe_flow_path: the fs_path value passed on flow create/update was falsy (empty string or whitespace-only handled as empty) before any path resolution is attempted. _get_safe_flow_path is the single sanitiser through which every flow fs_path must pass, so an empty value is rejected up front rather than resolving to the user's flows root.

Source

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

from langflow.services.deps import get_settings_service
from langflow.services.storage.service import StorageService

if TYPE_CHECKING:
    from langflow.services.database.models.user.model import User


def _get_safe_flow_path(fs_path: str, user_id: UUID, storage_service: StorageService) -> Path:
    """Get a safe filesystem path for flow storage, restricted to user's flows directory.

    Allows both absolute and relative paths, but ensures they're within the user's flows directory.

    Uses ``os.path.realpath`` + ``startswith`` for containment — the sanitiser pattern
    recognised by CodeQL's ``py/path-injection`` analysis. ``realpath`` canonicalises
    the path and follows symlinks, so the returned path is safe to pass to filesystem
    operations.
    """
    if not fs_path:
        raise HTTPException(status_code=400, detail="fs_path cannot be empty")

    # Normalize path separators first (before security checks to prevent backslash bypass)
    normalized_path = fs_path.replace("\\", "/")

    # Reject directory traversal and null bytes (check normalized path)
    if ".." in normalized_path:
        raise HTTPException(
            status_code=400,
            detail="Invalid fs_path: directory traversal (..) is not allowed",
        )
    if "\x00" in normalized_path:
        raise HTTPException(
            status_code=400,
            detail="Invalid fs_path: null bytes are not allowed",
        )

    # Build and canonicalise the safe base directory path.
    base_dir = storage_service.data_dir / "flows" / str(user_id)

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Omit the fs_path key entirely, or send null — None is explicitly allowed and means DB-only storage.
  2. Set fs_path to a real relative filename, e.g. "my_flow.json"; it is resolved under <data_dir>/flows/<user_id>/.
  3. Fix the client-side template/variable that produced the empty string.

Example fix

// before
{"name": "My Flow", "fs_path": ""}
// after
{"name": "My Flow", "fs_path": "my_flow.json"}
Defensive patterns

Strategy: validation

Validate before calling

if (!fsPath || !fsPath.trim()) throw new Error('fs_path must be a non-empty filename');

Type guard

const isValidFsPath = (p: unknown): p is string => typeof p === 'string' && p.trim().length > 0;

Prevention

When it happens

Trigger: POST/PATCH /api/v1/flows with body {"fs_path": ""} (or the string arriving empty after client-side templating), causing _verify_fs_path -> _get_safe_flow_path to hit the `if not fs_path` guard.

Common situations: Client code interpolates fs_path from a variable that is undefined/empty ("flows/{id}.json" template with missing id); a form field submitted blank; JSON where fs_path is "" instead of being omitted or set to null.

Related errors


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