{"record":{"id":"b25ceb024f88c0cb","repo":"roboflow/supervision","slug":"all-metadata-dictionaries-must-have-the-same-keys","errorCode":null,"errorMessage":"All metadata dictionaries must have the same keys to merge.","messagePattern":"All metadata dictionaries must have the same keys to merge\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/utils/internal.py","lineNumber":624,"sourceCode":"    Warning: Assumes that empty detections were filtered-out before passing metadata to\n    this function.\n\n    Args:\n        metadata_list: A list of metadata dictionaries to merge.\n\n    Returns:\n        A single merged metadata dictionary.\n\n    Raises:\n        ValueError: If there are conflicting values for the same key or if\n        dictionaries have different keys.\n    \"\"\"\n    if not metadata_list:\n        return {}\n\n    all_keys_sets = [set(metadata.keys()) for metadata in metadata_list]\n    if not all(keys_set == all_keys_sets[0] for keys_set in all_keys_sets):\n        raise ValueError(\"All metadata dictionaries must have the same keys to merge.\")\n\n    merged_metadata: _MetadataType = {}\n    for metadata in metadata_list:\n        for key, value in metadata.items():\n            if key not in merged_metadata:\n                merged_metadata[key] = value\n                continue\n\n            other_value = merged_metadata[key]\n            if isinstance(value, np.ndarray) and isinstance(other_value, np.ndarray):\n                if not np.array_equal(merged_metadata[key], value):\n                    raise ValueError(\n                        f\"Conflicting metadata for key: '{key}': \"\n                        f\"{type(value)}, {type(other_value)}.\"\n                    )\n            elif isinstance(value, np.ndarray) or isinstance(other_value, np.ndarray):\n                # Since [] == np.array([]).\n                raise ValueError(","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/utils/internal.py#L606-L642","documentation":"Raised by merge_metadata when concatenating Detections objects whose metadata dictionaries do not carry exactly the same set of keys. Metadata (e.g. video_id, source_path) must be homogeneous across all Detections being merged, because the merge cannot decide what value to use for a key that is missing in some inputs.","triggerScenarios":"Detections.merge([...]) or Detections.concatenate([...]) (core.py:2495), Detections.__add__ path, merge of two single-detection Detections (core.py:3462), or annotations.merge() (core.py:3366) where one Detections was built with metadata={'video_id': 1} and another with metadata={} or a differently-keyed dict.","commonSituations":"Concatenating per-frame Detections in a loop where some frames got metadata and others did not; merging detections from two different models/pipelines that attach different metadata keys; incrementally adding a new metadata key to an existing pipeline while old pickled/cached Detections lack it.","solutions":["Make every Detections carry the identical metadata keys, filling missing ones with a default (e.g. metadata={'video_id': d.metadata.get('video_id', -1)}) before merging","If the keys are intentionally different, strip metadata (rebuild Detections with metadata={}) before concatenating","Union the keys explicitly per input and fill defaults in your own pre-merge step so the library invariant (all_keys_sets equal) holds"],"exampleFix":"// before\nmerged = sv.Detections.merge([d1, d2])  # d1.metadata={'video_id':0}, d2.metadata={}\n// after\nkeys = d1.metadata.keys() | d2.metadata.keys()\nd1.metadata = {k: d1.metadata.get(k, None) for k in keys}\nd2.metadata = {k: d2.metadata.get(k, None) for k in keys}\nmerged = sv.Detections.merge([d1, d2])","handlingStrategy":"validation","validationCode":"def assert_mergeable_metadata(detections_list):\n    key_sets = [set(d.metadata.keys()) for d in detections_list]\n    first = key_sets[0]\n    for d, ks in zip(detections_list, key_sets):\n        assert ks == first, f\"metadata keys differ: {ks} vs {first} (obj {d})\"","typeGuard":null,"tryCatchPattern":"try:\n    merged = sv.Detections.merge(detections_list)\nexcept ValueError as e:\n    if \"same keys to merge\" in str(e):\n        keys = set().union(*(d.metadata.keys() for d in detections_list))\n        for d in detections_list:\n            d.metadata = {k: d.metadata.get(k, None) for k in keys}\n        merged = sv.Detections.merge(detections_list)\n    else:\n        raise","preventionTips":["Always populate metadata with a fixed schema (same keys, default None) at Detections construction sites","Write a small merge_detections wrapper that normalizes metadata keys and values before calling sv.Detections.merge","Add a unit test that merges two Detections built by different connectors to catch schema drift"],"tags":["detections","metadata","merge","validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}