chroma-core/chroma · warning · ChromaRateLimitError
Rate limit exceeded
Error message
Rate limit exceeded
What it means
Thrown by chromaFetch (chroma-fetch.ts:138) as a ChromaRateLimitError when the server returns 429 without the Backoff marker. It is the plain rate-limit response: too many requests in the window for the key/tenant. Unlike ChromaBackoffError there is no server-advised strategy, so the client must apply its own backoff and pacing.
Source
Thrown at clients/new-js/packages/chromadb/src/chroma-fetch.ts:138
} 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
- Batch writes (add/upsert accept arrays) to cut request counts by orders of magnitude.
- Add exponential backoff with jitter on ChromaRateLimitError before retrying.
- Cap concurrency with a semaphore (e.g. p-limit) and add client-side rate pacing.
- Split traffic across keys/tenants or request a limit increase if the load is legitimate.
Example fix
// before
for (const doc of docs) await collection.add({ ids: [doc.id], documents: [doc.text] }); // 429
// after
await collection.add({ ids: docs.map(d => d.id), documents: docs.map(d => d.text) }); // one batched request Defensive patterns
Strategy: retry
Validate before calling
import pLimit from "p-limit"; const limit = pLimit(4); // stay comfortably below the tenant's request/s limit await Promise.all(batches.map(b => limit(() => collection.add(b))));
Try / catch
try {
await collection.add(batch);
} catch (e) {
if (e instanceof ChromaRateLimitError) {
await new Promise(r => setTimeout(r, 1000 + Math.random() * 1000));
return collection.add(batch); // single retry; fix pacing if it recurs
}
throw e;
} Prevention
- Batch writes into single add/upsert calls instead of per-document requests.
- Apply client-side rate limiting matched to your tenant's limits.
- Alert when 429s exceed a threshold — it indicates structural under-provisioning, not bad luck.
When it happens
Trigger: Exceeding request-rate limits on Chroma Cloud (or a rate-limiting proxy in front of self-hosted Chroma): tight loops of queries, one-request-per-document writes, or monitoring endpoints polled too frequently.
Common situations: Per-item upsert loops instead of batching; aggressive polling of listCollections/heartbeat; load tests without pacing; shared keys across many services summing over the limit.
Related errors
- Backoff and retry
- conditional write conflict
- stale read
- ${response.status}: ${response.statusText}
- Failed to generate embeddings: {str(e)}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/f4ac8d2affd439b0.
Report an issue: GitHub.