langflow-ai/langflow · warning · HTTPException

Per-file metadata is not valid JSON: {exc.msg}

Error message

Per-file metadata is not valid JSON: {exc.msg}

What it means

parse_per_file_metadata json.loads()es the `per_file_metadata` form field; a syntax error in the JSON raises 422 'Per-file metadata is not valid JSON: {parser message}' with the JSONDecodeError chained. This is distinct from error 155 only in field name — the expected shape is {filename: {metadata object}, ...}.

Source

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

        msg = f"Metadata is not valid JSON: {exc.msg}"
        raise HTTPException(status_code=422, detail=msg) from exc
    return validate_user_metadata(decoded)


def parse_per_file_metadata(raw: str | None) -> dict[str, dict[str, Any]]:
    """Decode + validate the ``per_file_metadata`` form field.

    Shape: ``{filename: {metadata_dict}, ...}``. Each inner dict goes through
    the same validator as run-level metadata, so per-file overrides obey the
    same key/value rules. Empty/None → ``{}``.
    """
    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. Construct with json.dumps({fname: meta_dict, ...}).
  2. Ensure values are dicts BEFORE dumps — json.dumps cannot express a Python dict key that is fine but callers sometimes pass lists of (name, meta) tuples; convert those to a dict first.
  3. Check the embedded parser message for the exact offset and fix that spot.
  4. Omit the field entirely when there are no per-file overrides (empty/None -> {}).

Example fix

# before
form.add_field('per_file_metadata', str(per_file))  # repr -> 422

# after
form.add_field('per_file_metadata', json.dumps(per_file))  # e.g. {"a.pdf": {"dept": "fin"}}
Defensive patterns

Strategy: validation

Validate before calling

import json

def safe_per_file_field(per_file: dict | None) -> str:
    if not per_file:
        return ''
    assert all(isinstance(k, str) and k and isinstance(v, dict) for k, v in per_file.items())
    return json.dumps(per_file)

Type guard

def is_valid_per_file_json(raw: str) -> bool:
    if not raw:
        return True
    try:
        d = json.loads(raw)
    except json.JSONDecodeError:
        return False
    return isinstance(d, dict)

Try / catch

try:
    parse_per_file_metadata(raw)
except HTTPException as e:
    if e.status_code == 422 and 'Per-file metadata is not valid JSON' in e.detail:
        raise ValueError(f'rebuild field with json.dumps: {e.detail}') from e
    raise

Prevention

When it happens

Trigger: per_file_metadata set to a malformed string — unquoted keys, single quotes, trailing commas, or a JSON array of objects instead of an object keyed by filename (the array case parses fine and then fails error 157). Embedded parser message (e.g. 'Expecting value: line 1 column 1') identifies the offset.

Common situations: Building the per-file map by string concatenation ('{' + ','.join(parts) + '}'); double-encoding (a string that is itself a JSON string); clients that serialize {filename: dict} with a non-JSON serializer (Python repr); passing the whole request body as the field by mistake.

Related errors


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