langflow-ai/langflow · warning · HTTPException

Per-file metadata keys must be non-empty filename strings.

Error message

Per-file metadata keys must be non-empty filename strings.

What it means

Inside the per_file_metadata loop, each top-level key must be a non-empty string (a filename). JSON keys from json.loads are always strings, so the reachable condition is the empty-string key '' — 422 'Per-file metadata keys must be non-empty filename strings.' After this, each value must be an object (validated by validate_user_metadata), so a non-dict inner value fails errors 151/153/etc. instead.

Source

Thrown at src/backend/base/langflow/api/utils/kb_metadata.py:129

    """
    if not raw:
        return {}
    try:
        decoded = json.loads(raw)
    except json.JSONDecodeError as exc:
        msg = f"Per-file metadata is not valid JSON: {exc.msg}"
        raise HTTPException(status_code=422, detail=msg) from exc
    if not isinstance(decoded, dict):
        msg = "Per-file metadata must be a JSON object keyed by filename."
        raise HTTPException(status_code=422, detail=msg)
    if len(decoded) > KB_METADATA_MAX_KEYS:
        msg = f"Per-file metadata exceeds the {KB_METADATA_MAX_KEYS} file limit."
        raise HTTPException(status_code=422, detail=msg)
    out: dict[str, dict[str, Any]] = {}
    for filename, file_metadata in decoded.items():
        if not isinstance(filename, str) or not filename:
            msg = "Per-file metadata keys must be non-empty filename strings."
            raise HTTPException(status_code=422, detail=msg)
        if not isinstance(file_metadata, dict):
            msg = f"Per-file metadata for {filename!r} must be a JSON object."
            raise HTTPException(status_code=422, detail=msg)
        out[filename] = validate_user_metadata(file_metadata)
    return out

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Skip entries with falsy filenames when building the map: {k: v for k, v in per_file.items() if k}.
  2. Fix the source: assert the filename is non-empty when you read the upload part, and log/skip rows missing it.
  3. Match keys exactly to the uploaded files' names (the overrides are looked up per file; an empty key could never match anyway).
  4. Add a client-side guard: all(isinstance(k, str) and k for k in per_file).

Example fix

# before
per_file[name] = meta  # name == '' for missing rows -> 422

# after
if name:
    per_file[name] = meta
else:
    logger.warning('skipping metadata row without filename')
Defensive patterns

Strategy: validation

Validate before calling

def clean_per_file_keys(per_file: dict) -> dict:
    dropped = [k for k in per_file if not (isinstance(k, str) and k)]
    if dropped:
        logger.warning('dropping metadata entries with empty filenames: %r', dropped)
    return {k: v for k, v in per_file.items() if isinstance(k, str) and k}

Type guard

def has_valid_filename_keys(per_file) -> bool:
    return all(isinstance(k, str) and k for k in per_file)

Try / catch

try:
    parse_per_file_metadata(raw)
except HTTPException as e:
    if e.status_code == 422 and 'non-empty filename strings' in e.detail:
        per_file = clean_per_file_keys(per_file)
        raw = json.dumps(per_file)
        parse_per_file_metadata(raw)
    else:
        raise

Prevention

When it happens

Trigger: per_file_metadata JSON containing an empty-string key: {"": {"dept": "fin"}}. Typically produced when the filename variable was empty/None at build time (os.path.basename of a directory path, a blank spreadsheet cell, or an unpopulated form field).

Common situations: Path handling bugs: basename('/uploads/') returns ''; iterating rows where the filename column is missing; defaulting an unknown filename to '' instead of skipping; filename lost when a file part is named differently than expected and code falls back to ''.

Related errors


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