hcengineering/platform · warning · Error
rate-limit
Error message
rate-limit
What it means
checkRateLimits detects an HTTP 429 from the server, sleeps for the server-indicated wait time (Retry-After-ms, Retry-After, or X-RateLimit-Reset; default 1s), then throws an Error with the rateLimitError message ('rate-limit'). It signals the API quota was exhausted for this client.
Source
Thrown at foundations/core/packages/api-client/src/rest/rest.ts:196
}
private async checkRateLimits (response: Response): Promise<void> {
if (response.status === 429) {
// Extract rate limit information from headers
const retryAfter = response.headers.get('Retry-After')
const retryAfterMS = response.headers.get('Retry-After-ms')
const rateLimitReset = response.headers.get('X-RateLimit-Reset')
this.updateRateLimit(response)
const waitTime =
(retryAfterMS != null ? parseInt(retryAfterMS) : undefined) ??
(retryAfter != null
? parseInt(retryAfter) * 1000
: rateLimitReset != null
? new Date(parseInt(rateLimitReset)).getTime() - Date.now()
: 1000) // Default to 1 seconds if no headers are provided
await new Promise((resolve) => setTimeout(resolve, waitTime))
throw new Error(rateLimitError)
}
}
async getAccount (): Promise<Account> {
const requestUrl = concatLink(this.endpoint, `/api/v1/account/${this.workspace}`)
await this.checkRate()
const result = await withRetry<Account & { error?: Status }>(async () => {
const response = await fetch(requestUrl, this.requestInit())
if (!response.ok) {
await this.checkRateLimits(response)
throw new PlatformError(unknownError(response.statusText))
}
this.updateRateLimit(response)
return await extractJson<Account>(response)
})
if (result.error !== undefined) {
throw new PlatformError(result.error)
}View on GitHub (pinned to 63e28dc964)
Solutions
- Catch the error and retry after the delay indicated by Retry-After / X-RateLimit-Reset headers (the client already waited once — add your own backoff on top).
- Reduce request rate: batch requests, add throttling/debounce in loops.
- Respect the client's built-in slowdown (checkRate) rather than creating parallel clients bypassing it.
- Request a higher quota from the server admin or distribute work across tokens/workspaces.
Example fix
// before
for (const q of queries) await client.searchFulltext(q, {}) // slams the API
// after
for (const q of queries) {
try { await client.searchFulltext(q, {}) }
catch (e) { if (String(e.message).includes('rate-limit')) await sleep(5000) }
await sleep(200)
} Defensive patterns
Strategy: retry
Validate before calling
// Throttle proactively using the last observed rate-limit state
const remaining = Number(lastResponse?.headers?.get('X-RateLimit-Remaining') ?? '100')
if (remaining < 10) await sleep(5000) // back off before hitting 429 Type guard
function isRateLimitError(e: unknown): e is Error {
return e instanceof Error && e.message.includes('rate-limit')
} Try / catch
async function withRateLimitRetry<T>(fn: () => Promise<T>, attempts = 5): Promise<T> {
for (let i = 0; ; i++) {
try { return await fn() }
catch (e) {
if (isRateLimitError(e) && i < attempts - 1) { await sleep(2000 * 2 ** i); continue }
throw e
}
}
} Prevention
- Serialize requests instead of firing them in parallel; add per-request delay in bulk jobs
- Monitor X-RateLimit-Remaining and back off before exhausting the quota
- Retry with exponential backoff and honor Retry-After headers
- Distribute heavy batch workloads across time windows or multiple accounts
When it happens
Trigger: Any REST call (e.g. searchFulltext, domainRequest, getAccount) returning status 429 after exceeding the server's X-RateLimit quota; tight loops issuing many requests with one shared token.
Common situations: Bulk scripts iterating thousands of objects, background jobs hammering searchFulltext, multiple processes sharing one token, retry loops without backoff amplifying load.
Related errors
- response.statusText
- Failed to fetch config
- unknownError(response.statusText)
- Missing response body
- text (response body)
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/2f11904a002bf160.
Report an issue: GitHub.