MemPalace/mempalace · error · EmbeddingAPIError

Embedding API at {self._url} returned a non-object response:

Error message

Embedding API at {self._url} returned a non-object response: {data}

What it means

Raised by _vectors_from_response when the parsed JSON body of an /v1/embeddings response is not a top-level object (dict). A conformant server returns {"data": [...]}; anything else — a bare list, a string, a number, null — cannot be validated further and would otherwise crash later with a cryptic KeyError or numpy error. The whole offending payload is echoed in the message for diagnosis.

Source

Thrown at mempalace/embedding.py:580

                ) from e
            out.extend(self._vectors_from_response(data, len(batch)))
        return out

    def _vectors_from_response(self, data, n: int) -> list:
        """Validate one ``/v1/embeddings`` response and return L2-normed vectors.

        Guards every way a non-conformant server could corrupt the store
        silently: a missing/short ``data`` array, response ``index`` values
        that aren't the contiguous ``0..n-1`` batch positions (sorting then
        zipping positionally would otherwise misalign vectors with texts), and
        malformed / ragged / base64 embedding payloads. All failures raise
        :class:`EmbeddingAPIError` naming the endpoint rather than a cryptic
        numpy error — a silent wrong result would break the 100%-recall promise.
        """
        import numpy as np

        if not isinstance(data, dict):
            raise EmbeddingAPIError(
                f"Embedding API at {self._url} returned a non-object response: {data}"
            )
        rows = data.get("data")
        if not isinstance(rows, list):
            raise EmbeddingAPIError(
                f"Embedding API at {self._url} returned no 'data' array: {data.get('error', data)}"
            )
        if len(rows) != n:
            raise EmbeddingAPIError(
                f"Embedding API at {self._url} returned {len(rows)} embeddings for {n} inputs"
            )
        # The endpoint may return rows out of order — sort by index, then
        # require the indices to be exactly 0..n-1 so positional alignment is
        # provably correct (a server using absolute or duplicate indices would
        # otherwise pass the count check yet map vectors to the wrong texts).
        try:
            rows = sorted(rows, key=lambda d: d.get("index", -1))
            indices = [r.get("index") for r in rows]

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Curl the endpoint manually and confirm the body shape is a JSON object containing a 'data' array
  2. Point MEMPALACE_EMBEDDING_API_URL at a real OpenAI-compatible /v1/embeddings endpoint
  3. If writing a stub server, wrap results: {"data": [{"index": 0, "embedding": [...]}, ...]}
  4. Check for a proxy or gateway rewriting responses

Example fix

# stub server — before
return JsonResponse([{"index": 0, "embedding": vec}])
# after
return JsonResponse({"data": [{"index": 0, "embedding": vec}]})
Defensive patterns

Strategy: validation

Validate before calling

import json, urllib.request

def probe_embeddings_endpoint(url: str, model: str) -> bool:
    payload = json.dumps({"model": model, "input": ["probe"], "encoding_format": "float"}).encode()
    req = urllib.request.Request(url.rstrip('/') , data=payload, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=10) as r:
        return isinstance(json.loads(r.read()), dict)  # must be a top-level object

Type guard

def is_embeddings_response_shape(data) -> bool:
    return isinstance(data, dict) and isinstance(data.get("data"), list)

Try / catch

try:
    vecs = ef(texts)
except EmbeddingAPIError as e:
    if "non-object response" in str(e):
        log.error("endpoint %s is not OpenAI-compatible", url)  # config error: fix the URL
    raise

Prevention

When it happens

Trigger: The configured openai-compat endpoint returns a JSON array, a plain string, or an error page parsed as JSON — e.g. pointing MEMPALACE_EMBEDDING_API_URL at a non-OpenAI REST service, a misrouted reverse proxy that returns a status payload, or a mock/stub server used in tests.

Common situations: URL points at the wrong API (e.g. a models list endpoint or a management API); a gateway (nginx traefik) returns a JSON status object on error paths; hand-rolled stub servers during development that return [{...}] instead of {"data": [...]}

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/55e39160e901cdcb. Report an issue: GitHub.