langflow-ai/langflow · warning · HTTPException
Per-file metadata must be a JSON object keyed by filename.
Error message
Per-file metadata must be a JSON object keyed by filename.
What it means
After successful JSON decoding, parse_per_file_metadata requires the top-level value to be a JSON object keyed by filename ({filename: {...}}). An array, string, number, or null yields 422 'Per-file metadata must be a JSON object keyed by filename.' — this fires only for valid JSON of the wrong shape (invalid JSON fails earlier with error 156).
Source
Thrown at src/backend/base/langflow/api/utils/kb_metadata.py:121
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
- Convert pairs to a dict before serialization: dict(list_of_pairs) then json.dumps.
- Validate shape client-side: isinstance(payload, dict) and all(isinstance(v, dict) for v in payload.values()).
- Keep filenames as the keys, not duplicated inside the values.
- If you only have run-level metadata, use the `metadata` field instead and omit per_file_metadata.
Example fix
# before
per_file = [(name, meta) for name, meta in rows]
form.add_field('per_file_metadata', json.dumps(per_file)) # array -> 422
# after
form.add_field('per_file_metadata', json.dumps(dict(per_file))) Defensive patterns
Strategy: type-guard
Validate before calling
def to_per_file_map(pairs_or_map) -> dict[str, dict]:
m = dict(pairs_or_map) if not isinstance(pairs_or_map, dict) else pairs_or_map
assert all(isinstance(k, str) and k and isinstance(v, dict) for k, v in m.items()), 'need {filename: {...}}'
return m Type guard
def is_per_file_object(decoded) -> bool:
return isinstance(decoded, dict) and all(isinstance(v, dict) for v in decoded.values()) Try / catch
try:
parse_per_file_metadata(raw)
except HTTPException as e:
if e.status_code == 422 and 'must be a JSON object keyed by filename' in e.detail:
decoded = dict(decoded) # was an array of pairs; re-parse as map
raw = json.dumps(decoded)
parse_per_file_metadata(raw)
else:
raise Prevention
- Serialize a dict, not a list of tuples/records.
- Client-side assert isinstance(payload, dict).
- Filenames are keys only — don't also nest them inside values.
When it happens
Trigger: per_file_metadata = '[{"a.pdf": {...}}, {"b.pdf": {...}}]' (array of pairs, common when built from a list) or '"a.pdf:..."' (a bare string). Each inner value is then separately required to be an object (error at 159), but the outer shape check fires here first.
Common situations: Serializing a list of (filename, metadata) tuples with json.dumps (produces an array); a YAML-to-JSON converter emitting a sequence; per-file rows from a dataframe converted as records=[]; clients reusing a generic array payload format.
Related errors
- Metadata must be a JSON object.
- Metadata is not valid JSON: {exc.msg}
- Per-file metadata is not valid JSON: {exc.msg}
- Metadata value for '{key}' exceeds {KB_METADATA_MAX_VALUE_LE
- Metadata array '{key}' exceeds {KB_METADATA_MAX_ARRAY_LENGTH
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/5f563e8419a2881b.
Report an issue: GitHub.