{"record":{"id":"494d14bf91786b25","repo":"langflow-ai/langflow","slug":"metadata-is-not-valid-json-exc-msg","errorCode":null,"errorMessage":"Metadata is not valid JSON: {exc.msg}","messagePattern":"Metadata is not valid JSON: (.+?)","errorType":"validation","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"src/backend/base/langflow/api/utils/kb_metadata.py","lineNumber":101,"sourceCode":"                \"lowercase alphanumeric or underscore characters.\"\n            )\n            raise HTTPException(status_code=422, detail=msg)\n        if key in KB_METADATA_RESERVED_KEYS:\n            msg = f\"Metadata key '{key}' is reserved for ingestion-internal use.\"\n            raise HTTPException(status_code=422, detail=msg)\n        _validate_value(key, value)\n    return metadata\n\n\ndef parse_user_metadata(raw: str | None) -> dict[str, Any]:\n    \"\"\"Decode + validate the ``metadata`` form field. Empty/None → ``{}``.\"\"\"\n    if not raw:\n        return {}\n    try:\n        decoded = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        msg = f\"Metadata is not valid JSON: {exc.msg}\"\n        raise HTTPException(status_code=422, detail=msg) from exc\n    return validate_user_metadata(decoded)\n\n\ndef parse_per_file_metadata(raw: str | None) -> dict[str, dict[str, Any]]:\n    \"\"\"Decode + validate the ``per_file_metadata`` form field.\n\n    Shape: ``{filename: {metadata_dict}, ...}``. Each inner dict goes through\n    the same validator as run-level metadata, so per-file overrides obey the\n    same key/value rules. Empty/None → ``{}``.\n    \"\"\"\n    if not raw:\n        return {}\n    try:\n        decoded = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        msg = f\"Per-file metadata is not valid JSON: {exc.msg}\"\n        raise HTTPException(status_code=422, detail=msg) from exc\n    if not isinstance(decoded, dict):","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/utils/kb_metadata.py#L83-L119","documentation":"parse_user_metadata json.loads()es the raw multipart `metadata` form field; if the string is not valid JSON (syntax error), it raises 422 with 'Metadata is not valid JSON: {parser message}' chaining the JSONDecodeError. Empty or missing field is fine (returns {}); this error means a non-empty, syntactically broken payload.","triggerScenarios":"metadata form field set to 'project=alpha' (query-string style), '{project: \"alpha\"}' (unquoted key), a trailing comma, or truncated JSON from string slicing. json.loads' own message ('Expecting property name enclosed in double quotes', 'Unterminated string starting at...') is embedded, pinpointing the syntax position.","commonSituations":"Hand-building the field with f-strings or manual concatenation instead of json.dumps; curl --form with missing quotes; content mutated by a gateway or form encoder (unescaped quotes); copied payloads that were pretty-printed then re-wrapped in extra quotes once too many times.","solutions":["Always build the field with json.dumps(obj) — never f-strings or str(dict) (single quotes are not JSON).","Read the parser message in the 422 detail: it states exactly what was expected at the failing offset.","If the field may be empty, omit it or send '' (both map to {}), rather than 'null' text handled elsewhere.","Test round-trip locally: json.loads(json.dumps(meta)) before sending."],"exampleFix":"# before\nform.add_field('metadata', str({'a': 1}))  # \"{'a': 1}\" -> 422\n\n# after\nimport json\nform.add_field('metadata', json.dumps({'a': 1}))","handlingStrategy":"validation","validationCode":"import json\n\ndef safe_metadata_field(obj) -> str:\n    return json.dumps(obj) if obj else ''  # valid JSON or empty","typeGuard":"def is_valid_metadata_json(raw: str) -> bool:\n    if not raw:\n        return True\n    try:\n        json.loads(raw); return True\n    except json.JSONDecodeError:\n        return False","tryCatchPattern":"try:\n    parse_user_metadata(raw)\nexcept HTTPException as e:\n    if e.status_code == 422 and 'not valid JSON' in e.detail:\n        raise ValueError(f'fix JSON syntax: {e.detail}') from e\n    raise","preventionTips":["Never build the field with f-strings or str(dict); always json.dumps.","Round-trip check locally: json.loads(json.dumps(meta)).","Use the embedded parser message to locate the exact syntax offset."],"tags":["knowledge-base","metadata","json","validation","http-422"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}