langflow-ai/langflow · error · HTTPException

Invalid filename

Error message

Invalid filename

What it means

validate_public_files() rejects a file reference whose basename is exactly '.' after the path already passed the format and namespace checks. It is part of the hardening for GHSA-rcjh-r59h-gq37, which restricted unauthenticated public-flow builds to file paths of the form {source_flow_id}/{basename}. Although the '..' substring is already rejected earlier, a lone '.' basename (the current directory) is blocked here as an extra guard so anonymous callers cannot address the flow's directory itself.

Solutions

  1. Fix the client to send only plain basenames under the flow's own UUID, e.g. '{flow_id}/report.pdf' — never '.' or '..'.
  2. If you construct paths programmatically, split on '/' and drop empty components instead of using normpath before sending to the API.
  3. If you legitimately need directory-like grouping, upload under a flattened name (e.g. 'folder_report.pdf'); the API only accepts a single path segment.
  4. If you operate the server and see floods of these, treat them as probes of GHSA-rcjh-r59h-gq37 and rate-limit the endpoint.

Example fix

# before
files = [f"{flow_id}/{os.path.normpath(rel_path)}"]  # normpath('....') can yield '.'

# after
rel = rel_path.strip('/').split('/')[-1]  # keep basename only
if rel in ('.', '..'):
    raise ValueError(f"bad file path: {rel_path}")
files = [f"{flow_id}/{rel}"]
Defensive patterns

Strategy: validation

Validate before calling

import re
PATH_RE = re.compile(r'^([0-9a-fA-F-]{36})/([^/\\]+)$')

def safe_public_files(files, flow_id):
    fid = str(flow_id).lower()
    out = []
    for f in files or []:
        m = PATH_RE.match(f)
        if (not m or m.group(1).lower() != fid or m.group(2) in ('.', '..')
                or any(t in f for t in ('..', '\\', '\x00'))):
            raise ValueError(f'unsafe file entry: {f!r}')
        out.append(f)
    return out

Try / catch

try:
    validate_public_files(files, flow_id)
except HTTPException as e:
    if e.status_code == 400 and e.detail == 'Invalid filename':
        # strip '.'/'..' entries client-side and rebuild the list
        ...
    raise

Prevention

When it happens

Trigger: A caller of a public-flow build/upload endpoint passes a files list entry like '3f0b.../.' (flow UUID matching the public flow, basename exactly '.'). Note that entries containing '..' anywhere, backslashes, NUL bytes, non-UUID prefixes, or a foreign flow UUID fail earlier with 'Invalid file path'/'Invalid file path format'/'File not in this flow's namespace' instead.

Common situations: Client code that builds file paths by string concatenation (os.path.join or f-strings) and accidentally appends an empty segment; scripts replaying captured upload requests after path normalization (os.path.normpath turns './' into '.'); crafted probes against a shared public flow trying to escape the {flow_id}/ namespace.

Related errors


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

Appendix: source

Thrown at src/backend/base/langflow/api/utils/flow_utils.py:171

    Called from any endpoint that accepts caller-supplied file references
    under a public-access boundary.
    """
    if not files:
        return
    expected_flow_id = str(source_flow_id).lower()
    for entry in files:
        if not isinstance(entry, str) or not entry:
            raise HTTPException(status_code=400, detail="Invalid file entry")
        if any(token in entry for token in _PUBLIC_FILE_REJECTED_SUBSTRINGS):
            raise HTTPException(status_code=400, detail="Invalid file path")
        match = _PUBLIC_FILE_PATH_RE.match(entry)
        if not match:
            raise HTTPException(status_code=400, detail="Invalid file path format")
        flow_id_segment, basename = match.group(1), match.group(2)
        if flow_id_segment.lower() != expected_flow_id:
            raise HTTPException(status_code=400, detail="File not in this flow's namespace")
        if basename in (".", ".."):
            raise HTTPException(status_code=400, detail="Invalid filename")


def compute_virtual_flow_id(
    identifier: str | uuid.UUID,
    flow_id: uuid.UUID,
    *,
    principal_type: Literal["user", "client"] | None = None,
) -> uuid.UUID:
    """Compute a deterministic virtual flow ID for session/message isolation.

    Args:
        identifier: A unique identifier (user_id for authenticated users, client_id for anonymous).
        flow_id: The original flow ID.
        principal_type: Optional identity domain for public-flow callers. Authenticated
            user IDs and anonymous client IDs must never share a UUID namespace.

    Returns:
        A deterministic UUID v5 derived from the identifier and flow_id.

View on GitHub (pinned to 976ec789d2)