{"record":{"id":"b974d5f6ab76a727","repo":"HKUDS/DeepTutor","slug":"cannot-parse-embeddings-from-response-json-top-le","errorCode":null,"errorMessage":"Cannot parse embeddings from response JSON. Top-level keys={keys}, expected one of: data/embedding/embeddings/result/output.","messagePattern":"Cannot parse embeddings from response JSON\\. Top-level keys=(.+?), expected one of: data/embedding/embeddings/result/output\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/embedding/adapters/openai_compatible.py","lineNumber":141,"sourceCode":"        if isinstance(output, dict):\n            if isinstance(output.get(\"data\"), list):\n                candidates.append(output[\"data\"])\n            if isinstance(output.get(\"embeddings\"), list):\n                candidates.append(output[\"embeddings\"])\n\n        for c in candidates:\n            if not c:\n                continue\n            first = c[0]\n            # list of {\"embedding\":[...]}\n            if isinstance(first, dict) and \"embedding\" in first:\n                return [item.get(\"embedding\") or [] for item in c if isinstance(item, dict)]\n            # list of vectors [[...], ...]\n            if isinstance(first, list):\n                return [item for item in c if isinstance(item, list)]\n\n        keys = sorted(list(data.keys()))\n        raise ValueError(\n            \"Cannot parse embeddings from response JSON. \"\n            f\"Top-level keys={keys}, expected one of: data/embedding/embeddings/result/output.\"\n        )\n\n    _MAX_RETRIES = 5\n    _RETRY_BACKOFF = 1.0\n    _RATE_LIMIT_BACKOFF = 5.0\n\n    def _should_send_dimensions(self, model_name: str | None) -> bool:\n        \"\"\"Decide whether to attach `dimensions` to the request payload.\n\n        Tri-state semantics driven by `self.send_dimensions`:\n        * ``True``  -> always send (user explicitly opted in)\n        * ``False`` -> never send (user explicitly opted out)\n        * ``None``  -> auto: send for known model families that accept the\n          OpenAI-style ``dimensions`` parameter — OpenAI ``text-embedding-3*``,\n          Qwen3-Embedding, Qwen3-VL-Embedding.\n        \"\"\"","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/embedding/adapters/openai_compatible.py#L123-L159","documentation":"The response parsed as a JSON object, contained no \"error\" key, but none of the known vector-bearing shapes (data / embedding / embeddings / result / output) were present or non-empty. This means the endpoint answered with an unexpected schema — usually a chat-completion object, a usage-only object, or a gateway status page.","triggerScenarios":"base_url points at a chat/completions or models endpoint instead of /v1/embeddings; the gateway returns {\"object\": \"list\"} or metadata without vectors; empty \"data\": [] after filtering; the model is not an embedding model so the server returns something else entirely.","commonSituations":"base_url copy-pasted from an LLM chat config (missing /v1/embeddings suffix); pointing at Ollama's /api/chat; provider changed response schema; requesting a generative model on the embeddings route.","solutions":["Inspect the reported Top-level keys= list to identify what the endpoint actually returned","Fix base_url to end with the embeddings path (e.g. https://host/v1/embeddings)","Confirm the configured model is an embedding model, not a chat model","If a legitimate new schema, extend _extract_embeddings_from_response with the new key"],"exampleFix":"# before\nbase_url = \"https://my-gateway.example.com/v1\"  # chat endpoint, returns chat JSON\n# after\nbase_url = \"https://my-gateway.example.com/v1/embeddings\"","handlingStrategy":"validation","validationCode":"import httpx\nasync def check_embeddings_endpoint(base_url, api_key, model):\n    r = await httpx.AsyncClient().post(base_url, json={\"input\": [\"ping\"], \"model\": model},\n                                      headers={\"Authorization\": f\"Bearer {api_key}\"})\n    data = r.json()\n    assert not (isinstance(data, dict) and \"error\" in data), data\n    assert any(k in data for k in (\"data\", \"embedding\", \"embeddings\", \"result\", \"output\")), data","typeGuard":"null","tryCatchPattern":"try:\n    resp = await adapter.embed(req)\nexcept ValueError as e:\n    if \"Cannot parse embeddings\" in str(e):\n        # likely wrong endpoint URL — recheck base_url config\n        raise ConfigurationError(str(e)) from e\n    raise","preventionTips":["Always verify base_url ends with the embeddings path","Smoke-test one embed() call at startup before batch jobs"],"tags":["embeddings","response-schema","base-url","json-parsing"],"backgroundTag":"unexpected-response-schema","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}