{"record":{"id":"27aa325e49135e0f","repo":"RyanCodrai/turbovec","slug":"metadatas-i-must-be-a-dict-got-type-meta-n","errorCode":null,"errorMessage":"metadatas[{i}] must be a dict, got {type(meta).__name__}","messagePattern":"metadatas\\[(.+?)\\] must be a dict, got (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"turbovec-python/python/turbovec/langchain.py","lineNumber":375,"sourceCode":"    def _store_texts_and_vectors(\n        self,\n        texts_list: list[str],\n        vectors: np.ndarray,\n        metadatas: list[dict],\n        ids: list[str],\n    ) -> list[str]:\n        if vectors.ndim != 2:\n            raise ValueError(f\"expected 2D embedding batch, got {vectors.ndim}D\")\n\n        # Validate every metadata entry before touching any state. A bad\n        # entry (e.g. None) previously blew up as `dict(None)` mid-loop,\n        # *after* the vectors had been added to the index — leaving the\n        # docstore and index desynced in memory (and dump() would persist\n        # the corruption). The reference InMemoryVectorStore rejects bad\n        # metadata with a named error; mirror that here.\n        for i, meta in enumerate(metadatas):\n            if not isinstance(meta, dict):\n                raise TypeError(\n                    f\"metadatas[{i}] must be a dict, \"\n                    f\"got {type(meta).__name__}\"\n                )\n\n        # Dedup intra-batch duplicate ids, keeping the last occurrence —\n        # matches InMemoryVectorStore, whose dict store silently overwrites\n        # on a repeated id. Without this every row is added to the index but\n        # _str_to_u64 keeps only the last handle per id, orphaning the\n        # earlier vectors. The returned id list still mirrors the input\n        # (one entry per input text), as the reference does.\n        result_ids = ids\n        keep = resolve_duplicates(ids, DuplicatePolicy.KEEP_LAST)\n        if len(keep) != len(ids):\n            ids = [ids[i] for i in keep]\n            texts_list = [texts_list[i] for i in keep]\n            metadatas = [metadatas[i] for i in keep]\n            vectors = vectors[keep]\n","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/RyanCodrai/turbovec/blob/ccab9f325e6ce2a270a87daf01ae4e443bcf2d49/turbovec-python/python/turbovec/langchain.py#L357-L393","documentation":"TypeError raised in _store_texts_and_vectors when a metadatas entry is not a dict (e.g. None). Validation happens for the whole batch before any state change: previously a bad entry blew up as dict(None) mid-loop after vectors were added to the index, desyncing the docstore and index (and persisting the corruption via dump).","triggerScenarios":"Thrown at turbovec-python/python/turbovec/langchain.py:375 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Pass a dict (possibly empty) for every text's metadata, matching InMemoryVectorStore's contract.","Fix upstream Document construction that produced None metadata.","Catch the TypeError in ingestion code — it is raised before any mutation, so the store remains consistent."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"ccab9f325e6ce2a270a87daf01ae4e443bcf2d49","analyzedAt":"2026-09-06T08:39:18.516Z","contentChangedAt":"2026-09-06T08:39:18.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}