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

  1. Batch writes (add/upsert accept arrays) to cut request counts by orders of magnitude.
  2. Add exponential backoff with jitter on ChromaRateLimitError before retrying.
  3. Cap concurrency with a semaphore (e.g. p-limit) and add client-side rate pacing.
  4. 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

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


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