{"record":{"id":"6df533a5b936d6f5","repo":"langflow-ai/langflow","slug":"metadata-array-key-must-contain-only-strings","errorCode":null,"errorMessage":"Metadata array '{key}' must contain only strings.","messagePattern":"Metadata array '(.+?)' must contain only strings\\.","errorType":"validation","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"src/backend/base/langflow/api/utils/kb_metadata.py","lineNumber":57,"sourceCode":"    return all(c in _KEY_ALLOWED_CHARS for c in key)\n\n\ndef _validate_value(key: str, value: Any) -> None:\n    if isinstance(value, (bool, int, float)):\n        return\n    if isinstance(value, str):\n        if len(value) > KB_METADATA_MAX_VALUE_LENGTH:\n            msg = f\"Metadata value for '{key}' exceeds {KB_METADATA_MAX_VALUE_LENGTH} characters.\"\n            raise HTTPException(status_code=422, detail=msg)\n        return\n    if isinstance(value, list):\n        if len(value) > KB_METADATA_MAX_ARRAY_LENGTH:\n            msg = f\"Metadata array '{key}' exceeds {KB_METADATA_MAX_ARRAY_LENGTH} items.\"\n            raise HTTPException(status_code=422, detail=msg)\n        for entry in value:\n            if not isinstance(entry, str):\n                msg = f\"Metadata array '{key}' must contain only strings.\"\n                raise HTTPException(status_code=422, detail=msg)\n            if len(entry) > KB_METADATA_MAX_VALUE_LENGTH:\n                msg = f\"Metadata array entry under '{key}' exceeds {KB_METADATA_MAX_VALUE_LENGTH} characters.\"\n                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)","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/utils/kb_metadata.py#L39-L75","documentation":"Array-valued metadata must be homogeneous lists of strings. If any element of the list is not a str (e.g. an int, dict, or nested list), _validate_value rejects the whole field with 422. This keeps metadata flat and predictable for the vector-store filters and the chunks browser that consume it.","triggerScenarios":"A metadata JSON like {\"tags\": [\"ml\", 42]} or {\"refs\": [{\"id\": 1}]} in the metadata or per_file_metadata form field of a KB ingest request. Note bool is NOT an accepted array element (bool is a valid scalar type but arrays must be all-strings), and the type check runs before the per-entry length check.","commonSituations":"JSON produced by json.dumps of mixed Python lists; tagging systems that emit [id, label] pairs; LLM-generated metadata where the model mixes types; per-file metadata maps built generically from ORM objects.","solutions":["Coerce every array element to str before submitting: [str(x) for x in items].","Model numbers as separate scalar keys ({\"priority\": 3}) rather than inside arrays.","Validate the shape client-side with a JSON-schema or a small guard before the multipart POST (see defense section).","For dicts inside arrays, flatten to 'key:value' strings or store externally."],"exampleFix":"# before\nmetadata = {\"tags\": [\"nlp\", 42, None]}  # 422\n\n# after\nmetadata = {\"tags\": [str(t) for t in (\"nlp\", 42) if t is not None], \"rank\": 42}","handlingStrategy":"type-guard","validationCode":"def coerce_arrays(meta: dict) -> dict:\n    return {k: ([str(x) for x in v] if isinstance(v, list) else v) for k, v in meta.items()}","typeGuard":"def is_homogeneous_string_array(v) -> bool:\n    return not isinstance(v, list) or (len(v) <= 16 and all(isinstance(x, str) for x in v))","tryCatchPattern":"try:\n    validate_user_metadata(meta)\nexcept HTTPException as e:\n    if e.status_code == 422 and 'must contain only strings' in e.detail:\n        meta = {k: ([str(x) for x in v] if isinstance(v, list) else v) for k, v in meta.items()}\n        validate_user_metadata(meta)\n    else:\n        raise","preventionTips":["Run [str(x) for x in arr] on every array before submit.","Keep numeric data in scalar keys, never inside arrays.","Remember booleans are not valid array elements either."],"tags":["knowledge-base","metadata","validation","type-error","http-422"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}