{"record":{"id":"f4ac8d2affd439b0","repo":"chroma-core/chroma","slug":"rate-limit-exceeded","errorCode":null,"errorMessage":"Rate limit exceeded","messagePattern":"Rate limit exceeded","errorType":"http","errorClass":"ChromaRateLimitError","httpStatus":429,"severity":"warning","filePath":"clients/new-js/packages/chromadb/src/chroma-fetch.ts","lineNumber":138,"sourceCode":"      } catch (error) {\n        if (\n          error instanceof ChromaQuotaExceededError ||\n          error instanceof ChromaClientError\n        ) {\n          throw error;\n        }\n        throw new ChromaClientError(\n          `Unprocessable Entity: ${response.statusText}`,\n        );\n      }\n    case 429:\n      const rateLimitBody = await getErrorBody(response);\n      if (rateLimitBody.error === \"Backoff\") {\n        throw new ChromaBackoffError(\n          rateLimitBody.message || \"Backoff and retry\",\n        );\n      }\n      throw new ChromaRateLimitError(\"Rate limit exceeded\");\n  }\n\n  const errorMessage = await getErrorMessage(response);\n  throw new ChromaServerError(errorMessage);\n};\n","sourceCodeStart":120,"sourceCodeEnd":144,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/chroma-fetch.ts#L120-L144","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfor (const doc of docs) await collection.add({ ids: [doc.id], documents: [doc.text] }); // 429\n\n// after\nawait collection.add({ ids: docs.map(d => d.id), documents: docs.map(d => d.text) }); // one batched request","handlingStrategy":"retry","validationCode":"import pLimit from \"p-limit\";\nconst limit = pLimit(4); // stay comfortably below the tenant's request/s limit\nawait Promise.all(batches.map(b => limit(() => collection.add(b))));","typeGuard":null,"tryCatchPattern":"try {\n  await collection.add(batch);\n} catch (e) {\n  if (e instanceof ChromaRateLimitError) {\n    await new Promise(r => setTimeout(r, 1000 + Math.random() * 1000));\n    return collection.add(batch); // single retry; fix pacing if it recurs\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["http-429","rate-limit","retryable","throttling"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}