{"record":{"id":"f920d4113839bd4d","repo":"langflow-ai/langflow","slug":"metadata-exceeds-the-kb-metadata-max-keys-key-li","errorCode":null,"errorMessage":"Metadata exceeds the {KB_METADATA_MAX_KEYS} key limit.","messagePattern":"Metadata exceeds the (.+?) key limit\\.","errorType":"validation","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"src/backend/base/langflow/api/utils/kb_metadata.py","lineNumber":78,"sourceCode":"                raise HTTPException(status_code=422, detail=msg)\n        return\n    msg = f\"Metadata value for '{key}' must be a string, number, bool, or string array; got {type(value).__name__}.\"\n    raise HTTPException(status_code=422, detail=msg)\n\n\ndef validate_user_metadata(metadata: dict[str, Any]) -> dict[str, Any]:\n    \"\"\"Enforce the user-metadata contract on a decoded dict.\n\n    Returns the same dict (a shallow copy is *not* made — callers may mutate\n    safely once validation passes). Raises :class:`HTTPException` with a 422\n    status on any violation so FastAPI surfaces an inline error.\n    \"\"\"\n    if not isinstance(metadata, dict):\n        msg = \"Metadata must be a JSON object.\"\n        raise HTTPException(status_code=422, detail=msg)\n    if len(metadata) > KB_METADATA_MAX_KEYS:\n        msg = f\"Metadata exceeds the {KB_METADATA_MAX_KEYS} key limit.\"\n        raise HTTPException(status_code=422, detail=msg)\n    for key, value in metadata.items():\n        if not isinstance(key, str) or not _is_valid_key(key):\n            msg = (\n                f\"Metadata key {key!r} is invalid: must be 1-{KB_METADATA_MAX_KEY_LENGTH} \"\n                \"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 {}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/utils/kb_metadata.py#L60-L96","documentation":"A metadata object may contain at most KB_METADATA_MAX_KEYS = 16 keys. The count check runs immediately after the object-shape check and rejects the entire payload with 422 when exceeded, because every key/value pair is duplicated onto every chunk of the ingested file in the vector store.","triggerScenarios":"metadata (or any inner dict of per_file_metadata) is a JSON object with 17+ keys. The file-level count check for per_file_metadata (KB_METADATA_MAX_KEYS files) is a separate earlier error; this one counts keys inside a single metadata dict.","commonSituations":"Auto-extracted metadata (entities, attributes, dates, ...) with unbounded key counts; migrating a wide schema (CRM records, 30+ columns) into KB metadata; additive development where teams keep piling fields until the cap is hit.","solutions":["Cut the dict to the 16 keys you actually filter on client-side (prioritize keys used in queries).","Move rarely-filtered fields into the document content or an external store keyed by file id.","Use compact keys — but note key length is separately capped at 32 chars, so abbreviate within that.","Programmatically enforce: assert len(meta) <= 16 before building the multipart form."],"exampleFix":"# before\nmetadata = extracted  # 30 keys -> 422\n\n# after\nFILTER_KEYS = ['project', 'doctype', 'year']\nmetadata = {k: extracted[k] for k in FILTER_KEYS if k in extracted}  # <=16","handlingStrategy":"validation","validationCode":"from langflow.utils.kb_constants import KB_METADATA_MAX_KEYS as MAXK\n\ndef cap_keys(meta: dict, priority: list[str]) -> dict:\n    keep = [k for k in priority if k in meta][:MAXK]\n    keep += [k for k in meta if k not in keep and len(keep) < MAXK]\n    return {k: meta[k] for k in keep}","typeGuard":"def within_key_limit(meta) -> bool:\n    return isinstance(meta, dict) and len(meta) <= 16","tryCatchPattern":"try:\n    validate_user_metadata(meta)\nexcept HTTPException as e:\n    if e.status_code == 422 and 'key limit' in e.detail:\n        meta = cap_keys(meta, PRIORITY_KEYS); validate_user_metadata(meta)\n    else:\n        raise","preventionTips":["Fix a priority list of filterable keys and drop the rest.","Import KB_METADATA_MAX_KEYS instead of guessing the cap.","Move non-filter metadata into the document body or external storage."],"tags":["knowledge-base","metadata","validation","http-422","limits"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}