{"record":{"id":"fb5a1832ee5b6575","repo":"unslothai/unsloth","slug":"embedder-returned-ragged-vectors-shape-arr-shape","errorCode":null,"errorMessage":"embedder returned ragged vectors: shape {arr.shape}","messagePattern":"embedder returned ragged vectors: shape (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/rag/embed_llama_server.py","lineNumber":760,"sourceCode":"        rows: list[list[float]] = []\n        batch = max(1, config.EMBED_BATCH)\n        for start in range(0, n, batch):\n            chunk = list(texts[start : start + batch])\n            data = self._post(\n                \"/v1/embeddings\",\n                {\"input\": chunk, \"model\": \"embedding\", \"encoding_format\": \"float\"},\n            )\n            items = data.get(\"data\", [])\n            if len(items) != len(chunk):\n                raise RuntimeError(\n                    f\"embedder returned {len(items)} vectors for {len(chunk)} inputs\"\n                )\n            # OpenAI spec lets the server reorder; sort back by index.\n            items = sorted(items, key = lambda d: d.get(\"index\", 0))\n            rows.extend(d[\"embedding\"] for d in items)\n        arr = np.asarray(rows, dtype = np.float32)\n        if arr.ndim != 2:\n            raise RuntimeError(f\"embedder returned ragged vectors: shape {arr.shape}\")\n        if normalize:\n            norms = np.linalg.norm(arr, axis = 1, keepdims = True)\n            norms[norms == 0] = 1.0\n            arr = arr / norms\n        return arr\n\n    def dim(self, *, model_name = None) -> int:\n        \"\"\"Embedding width, probed via a 1-text encode and cached per model\n        (_resolve_model_path clears it when the effective repo changes).\n        Unlocked: concurrent probes are benign, and locking would deadlock when\n        the probe's encode respawns onto a changed model (see __init__).\"\"\"\n        self._ensure_ready()\n        cached = self._dim\n        if cached is not None:\n            return cached\n        vec = self.encode([\"x\"], normalize = False)\n        width = int(vec.shape[1])\n        self._dim = width","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/rag/embed_llama_server.py#L742-L778","documentation":"Raised after batching when the collected embedding rows cannot be stacked into a 2-D (N, dim) array — i.e. vectors have inconsistent lengths (ragged). np.asarray of rows with differing dims yields an object/array with ndim != 2, which the backend rejects because downstream cosine similarity and the vector store assume fixed-width rows. It indicates the server returned vectors of varying dimensionality, typically from a model swap or a malformed response.","triggerScenarios":"Multiple encode() batches in one call straddling a server restart onto a different GGUF with a different embedding dimension; a server bug returning truncated floats or wrong-length vectors for some inputs; mixing responses from different models when the server was externally restarted mid-call.","commonSituations":"Changing EMBED_MODEL_PATH while a worker is mid-ingest so early batches use the old dimension and later batches the new; concurrent processes each spawning servers with different models; a quantized GGUF whose output layer is inconsistent.","solutions":["Log arr.shape and the set of len(row) values to identify which batch produced odd-length vectors.","Ensure the embedding model (GGUF) cannot change mid-run — restart ingestion workers after changing EMBED_MODEL_PATH.","Verify the GGUF is a genuine embedding model with a fixed output dimension (probe with a single-text encode and check dim()).","Re-download the GGUF if corrupted; verify its checksum."],"exampleFix":"# before\narr = np.asarray(rows, dtype=np.float32)\n\n# after (diagnostic guard before stacking)\nwidths = {len(r) for r in rows}\nif len(widths) > 1:\n    raise RuntimeError(f\"ragged embedding widths {sorted(widths)}; server model changed mid-call?\")\narr = np.asarray(rows, dtype=np.float32)","handlingStrategy":"validation","validationCode":"widths = {len(r) for r in rows}\nif len(widths) != 1:\n    raise ValueError(f\"server returned mixed embedding widths {sorted(widths)}\")\n# only then stack\narr = np.asarray(rows, dtype=np.float32)","typeGuard":null,"tryCatchPattern":"try:\n    arr = np.asarray(rows, dtype=np.float32)\n    if arr.ndim != 2:\n        raise RuntimeError(f\"ragged vectors: shape {arr.shape}\")\nexcept RuntimeError:\n    # dimension drifted mid-call: restart worker so all batches use one model\n    request_backend_reload()","preventionTips":["Restart ingestion workers after changing the embedding model so all batches share one dimension.","Cache dim() once per run and assert every batch matches it before writing to the store.","Pin the embedding GGUF by checksum; a swapped file changes dimensions silently."],"tags":["embeddings","numpy","validation","data-integrity"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}