chroma-core/chroma · error · ChromaClientError

Unprocessable Entity: ${response.statusText}

Error message

Unprocessable Entity: ${response.statusText}

What it means

Thrown by chromaFetch (chroma-fetch.ts:127) as a ChromaClientError when the server returns 422 and even reading/parsing the body fails (response.json() throws and the caught error is not already a Chroma error). The message is synthesized as 'Unprocessable Entity: <statusText>', meaning content-level rejection with an unreadable body — typically an HTML error page from a proxy rather than Chroma's JSON.

Source

Thrown at clients/new-js/packages/chromadb/src/chroma-fetch.ts:127

      try {
        const body = await response.json();
        if (
          body &&
          body.message &&
          (body.message.startsWith("Quota exceeded") ||
            body.message.startsWith("Billing limit exceeded"))
        ) {
          throw new ChromaQuotaExceededError(body?.message);
        }
        throw new ChromaClientError(body?.message || "Unprocessable Entity");
      } catch (error) {
        if (
          error instanceof ChromaQuotaExceededError ||
          error instanceof ChromaClientError
        ) {
          throw error;
        }
        throw new ChromaClientError(
          `Unprocessable Entity: ${response.statusText}`,
        );
      }
    case 429:
      const rateLimitBody = await getErrorBody(response);
      if (rateLimitBody.error === "Backoff") {
        throw new ChromaBackoffError(
          rateLimitBody.message || "Backoff and retry",
        );
      }
      throw new ChromaRateLimitError("Rate limit exceeded");
  }

  const errorMessage = await getErrorMessage(response);
  throw new ChromaServerError(errorMessage);
};

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Bypass the proxy temporarily (hit the Chroma port directly) to confirm the 422 originates from Chroma itself.
  2. Check proxy/WAF logs and rules — especially request-size limits and body validation that can emit 422.
  3. Shrink the request batch size if large payloads trip the proxy.
  4. Reproduce with curl through the same proxy to capture the actual body.

Example fix

# before: nginx WAF returns HTML 422 for large batches
await collection.add(bigBatch); // 'Unprocessable Entity: ...'

# after: chunk the payload so it passes the proxy
for (const chunk of chunkBatch(bigBatch, 5000)) {
  await collection.add(chunk);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const MAX_BODY = 4 * 1024 * 1024; // keep under proxy body limits
if (JSON.stringify(batch).length > MAX_BODY) {
  throw new Error("batch too large for proxy; split before sending");
}

Try / catch

try {
  await collection.add(batch);
} catch (e) {
  if (e instanceof ChromaClientError && e.message.startsWith("Unprocessable Entity:")) {
    // body was not JSON: suspect proxy/WAF interception; curl through the same hop to confirm
  }
  throw e;
}

Prevention

When it happens

Trigger: A reverse proxy (nginx, ingress, Cloudflare) in front of Chroma intercepting the request and returning a 422 HTML/text page; truncated responses; content-encoding corruption making the body unparseable.

Common situations: Self-hosted Chroma behind an API gateway with body-validation WAF rules; proxies rejecting large embedding payloads; middleboxes mangling responses.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/fd8c60c5837e766d. Report an issue: GitHub.