{"record":{"id":"67e6d43041d04fa0","repo":"MemPalace/mempalace","slug":"embedding-api-at-self-url-returned-len-rows","errorCode":null,"errorMessage":"Embedding API at {self._url} returned {len(rows)} embeddings for {n} inputs","messagePattern":"Embedding API at (.+?) returned (.+?) embeddings for (.+?) inputs","errorType":"exception","errorClass":"EmbeddingAPIError","httpStatus":null,"severity":"error","filePath":"mempalace/embedding.py","lineNumber":589,"sourceCode":"        that aren't the contiguous ``0..n-1`` batch positions (sorting then\n        zipping positionally would otherwise misalign vectors with texts), and\n        malformed / ragged / base64 embedding payloads. All failures raise\n        :class:`EmbeddingAPIError` naming the endpoint rather than a cryptic\n        numpy error — a silent wrong result would break the 100%-recall promise.\n        \"\"\"\n        import numpy as np\n\n        if not isinstance(data, dict):\n            raise EmbeddingAPIError(\n                f\"Embedding API at {self._url} returned a non-object response: {data}\"\n            )\n        rows = data.get(\"data\")\n        if not isinstance(rows, list):\n            raise EmbeddingAPIError(\n                f\"Embedding API at {self._url} returned no 'data' array: {data.get('error', data)}\"\n            )\n        if len(rows) != n:\n            raise EmbeddingAPIError(\n                f\"Embedding API at {self._url} returned {len(rows)} embeddings for {n} inputs\"\n            )\n        # The endpoint may return rows out of order — sort by index, then\n        # require the indices to be exactly 0..n-1 so positional alignment is\n        # provably correct (a server using absolute or duplicate indices would\n        # otherwise pass the count check yet map vectors to the wrong texts).\n        try:\n            rows = sorted(rows, key=lambda d: d.get(\"index\", -1))\n            indices = [r.get(\"index\") for r in rows]\n        except AttributeError as e:\n            raise EmbeddingAPIError(\n                f\"Embedding API at {self._url} returned non-object rows: {e}\"\n            ) from e\n        if indices != list(range(n)):\n            raise EmbeddingAPIError(\n                f\"Embedding API at {self._url} returned non-contiguous or duplicate \"\n                f\"'index' values; cannot align embeddings with inputs\"\n            )","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/embedding.py#L571-L607","documentation":"Raised by _vectors_from_response when the 'data' array length does not equal the number of inputs sent in the batch (n). The client batches texts in groups of _EF_API_BATCH and expects exactly one embedding per input; a server that silently drops, truncates, or duplicates rows breaks the positional text-to-vector mapping, so the mismatch is treated as a hard failure rather than best-effort alignment.","triggerScenarios":"A server that caps response size, deduplicates identical input texts and returns fewer embeddings, streams partial results on error, or applies its own max batch limit smaller than mempalace's _EF_API_BATCH and truncates instead of erroring.","commonSituations":"Batch contains many duplicate strings and the server dedupes; a proxy truncates large JSON responses; llama.cpp server limits n_batch inputs; a stub returns a fixed single embedding regardless of input count.","solutions":["Check the message numbers (e.g. 'returned 1 embeddings for 8 inputs') — a 1-vs-n result usually means the server ignored the batch and embedded only the first text","Update or switch the embedding server to one that conforms to the OpenAI /v1/embeddings contract (one row per input)","If hosting your own server, verify it maps input array -> output array 1:1","Report a server-side max-batch limit if truncation is the cause; keep batches under it via the server config"],"exampleFix":"# stub server — before\nreturn {\"data\": [{\"index\": 0, \"embedding\": embed(texts[0])}]}\n# after\nreturn {\"data\": [{\"index\": i, \"embedding\": embed(t)} for i, t in enumerate(texts)]}","handlingStrategy":"validation","validationCode":"resp = probe(url, model, inputs=[\"a\", \"b\", \"c\"])\nassert len(resp[\"data\"]) == 3, f\"server returned {len(resp['data'])} rows for 3 inputs — not 1:1\"","typeGuard":"def is_one_to_one(data, n: int) -> bool:\n    return isinstance(data.get(\"data\"), list) and len(data[\"data\"]) == n","tryCatchPattern":"try:\n    vecs = ef(texts)\nexcept EmbeddingAPIError as e:\n    if \"embeddings for\" in str(e):\n        log.error(\"embedding server is not 1:1 per input; fix server before ingest\")\n    raise  # never retry: a wrong count means wrong alignment","preventionTips":["Never wrap this error in a retry — misaligned embeddings silently poison recall","Test your embedding server with a multi-text batch before bulk use","Prefer mainstream OpenAI-compatible servers over homegrown ones for embeddings"],"tags":["embedding","api","validation","batching"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}