langflow-ai/langflow · warning · HTTPException

Metadata key '{key}' is reserved for ingestion-internal use.

Error message

Metadata key '{key}' is reserved for ingestion-internal use.

What it means

Eight metadata keys are reserved for the ingestion pipeline itself (KB_METADATA_RESERVED_KEYS: source, file_name, chunk_index, total_chunks, ingested_at, job_id, source_type, source_metadata). If a user-supplied metadata dict uses any of them it is rejected with 422, because a user value would silently overwrite the ingestion-internal tag and break the chunks-browser filters keyed off them.

Source

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

    safely once validation passes). Raises :class:`HTTPException` with a 422
    status on any violation so FastAPI surfaces an inline error.
    """
    if not isinstance(metadata, dict):
        msg = "Metadata must be a JSON object."
        raise HTTPException(status_code=422, detail=msg)
    if len(metadata) > KB_METADATA_MAX_KEYS:
        msg = f"Metadata exceeds the {KB_METADATA_MAX_KEYS} key limit."
        raise HTTPException(status_code=422, detail=msg)
    for key, value in metadata.items():
        if not isinstance(key, str) or not _is_valid_key(key):
            msg = (
                f"Metadata key {key!r} is invalid: must be 1-{KB_METADATA_MAX_KEY_LENGTH} "
                "lowercase alphanumeric or underscore characters."
            )
            raise HTTPException(status_code=422, detail=msg)
        if key in KB_METADATA_RESERVED_KEYS:
            msg = f"Metadata key '{key}' is reserved for ingestion-internal use."
            raise HTTPException(status_code=422, detail=msg)
        _validate_value(key, value)
    return metadata


def parse_user_metadata(raw: str | None) -> dict[str, Any]:
    """Decode + validate the ``metadata`` form field. Empty/None → ``{}``."""
    if not raw:
        return {}
    try:
        decoded = json.loads(raw)
    except json.JSONDecodeError as exc:
        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.

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Rename your key to a non-reserved equivalent: 'origin', 'upstream_source', 'display_name', 'original_filename', etc. (still snake_case <=32).
  2. To derive per-file values, use per_file_metadata with non-reserved keys — the pipeline's own file_name is populated automatically.
  3. Keep a client-side denylist mirroring KB_METADATA_RESERVED_KEYS and strip/rename collisions before submit.
  4. If you truly need to override ingestion tags, that is not supported at the API boundary — file a feature request instead of working around it.

Example fix

# before
metadata = {"source": "crm", "file_name": f}  # 422 reserved

# after
metadata = {"origin": "crm", "original_filename": f}
Defensive patterns

Strategy: validation

Validate before calling

from langflow.utils.kb_constants import KB_METADATA_RESERVED_KEYS

def rename_reserved(meta: dict, mapping: dict) -> dict:
    return {mapping.get(k, k): v for k, v in meta.items() if k not in KB_METADATA_RESERVED_KEYS or k in mapping}

Type guard

def free_of_reserved_keys(meta) -> bool:
    return not (set(meta) & KB_METADATA_RESERVED_KEYS)

Try / catch

try:
    validate_user_metadata(meta)
except HTTPException as e:
    if e.status_code == 422 and 'reserved for ingestion-internal use' in e.detail:
        meta = rename_reserved(meta, {'source': 'origin', 'file_name': 'original_filename'})
        validate_user_metadata(meta)
    else:
        raise

Prevention

When it happens

Trigger: metadata or per_file_metadata contains e.g. {"source": "cms"} or {"file_name": "renamed.pdf"}. The reserved set is a frozenset in kb_constants.py; matching is exact and case-sensitive (so 'Source' instead fails the key-charset rule with error 153).

Common situations: Natural naming collisions — pipelines almost always want 'source' or 'file_name' as a user field; porting existing document metadata schemas; per-file overrides trying to rename the displayed filename.

Related errors


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