run-llama/llama_index · error · ValueError
Metadata key must be str!
Error message
Metadata key must be str!
What it means
`_validate_is_flat_dict` enforces that node metadata sent to a vector store is a flat dict with string keys. A non-str key can appear when integers or other hashables are used as dict keys (JSON round-trips sometimes introduce these), which most vector stores cannot index. The check runs inside `node_to_metadata_dict` with flat_metadata=True, so it fires at ingestion time, before anything is persisted.
Source
Thrown at llama-index-core/llama_index/core/vector_stores/utils.py:33
FilterOperator,
FilterCondition,
)
DEFAULT_TEXT_KEY = "text"
DEFAULT_TEXT_RESOURCE_KEY = "text_resource"
DEFAULT_EMBEDDING_KEY = "embedding"
DEFAULT_DOC_ID_KEY = "doc_id"
def _validate_is_flat_dict(metadata_dict: dict) -> None:
"""
Validate that metadata dict is flat,
and key is str, and value is one of (str, int, float, None).
"""
for key, val in metadata_dict.items():
if not isinstance(key, str):
raise ValueError("Metadata key must be str!")
if not isinstance(val, (str, int, float, type(None))):
raise ValueError(
f"Value for metadata {key} must be one of (str, int, float, None)"
)
def node_to_metadata_dict(
node: BaseNode,
remove_text: bool = False,
text_field: str = DEFAULT_TEXT_KEY,
text_resource_field: str = DEFAULT_TEXT_RESOURCE_KEY,
flat_metadata: bool = False,
) -> Dict[str, Any]:
"""Common logic for saving Node data into metadata dict."""
# Using mode="json" here because BaseNode may have fields of type bytes (e.g. images in ImageBlock),
# which would cause serialization issues.
node_dict = node.model_dump(mode="json")
metadata: Dict[str, Any] = node_dict.get("metadata", {})View on GitHub (pinned to afd0fef371)
Solutions
- Coerce all metadata keys to strings when building nodes: `{str(k): v for k, v in metadata.items()}`.
- Fix the upstream data source so keys are emitted as strings (e.g. `json.load(..., object_keys_hook=...)` or dataframe `astype(str)` on key columns).
- Add a validation step in your ingestion pipeline that rejects non-str keys early with a clearer message.
Example fix
# before
node = TextNode(text="...", metadata={101: "intro"}) # int key
# after
node = TextNode(text="...", metadata={str(k): v for k, v in raw_meta.items()}) Defensive patterns
Strategy: validation
Validate before calling
def normalize_metadata_keys(metadata: dict) -> dict:
bad = [k for k in metadata if not isinstance(k, str)]
if bad:
raise TypeError(f"non-str metadata keys: {bad!r}")
return metadata Type guard
def has_str_keys(metadata: dict) -> bool:
return all(isinstance(k, str) for k in metadata) Try / catch
try:
store.add(nodes)
except ValueError as e:
if "key must be str" in str(e):
nodes = [n.with metadata normalized] # fix keys, then retry once
raise Prevention
- Coerce keys with str(k) at the edge of your ingestion pipeline.
- Validate loaded JSON/dict metadata before constructing nodes.
- Add a schema check (pydantic model with str-keyed fields) for metadata.
When it happens
Trigger: Adding nodes whose `metadata` dict contains non-string keys (e.g. `{1: "chapter"}`) to a store that validates flat metadata; building Document/TextNode objects from pandas rows or JSON where numeric dict keys survive.
Common situations: Loading metadata from JSON files parsed with integer-like keys; converting dataframe columns to dicts and using column positions as keys; programmatic metadata builders that accept arbitrary dicts; Pydantic models dumping enums or other non-str key types.
Related errors
- Value for metadata {key} must be one of (str, int, float, No
- Metadata must be set
- Metadata must be set
- Tool name must be set
- spec_functions must be of type: List[Union[str, Tuple[str, s
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/d597e794bf5b6136.
Report an issue: GitHub.