{"record":{"id":"e1444c252412306c","repo":"alibaba/page-agent","slug":"rate-limit","errorCode":"RATE_LIMIT","errorMessage":"Rate limit exceeded: ${errorMessage}","messagePattern":"Rate limit exceeded: (.+?)","errorType":"http","errorClass":"InvokeError","httpStatus":429,"severity":"warning","filePath":"packages/llms/src/OpenAIClient.ts","lineNumber":109,"sourceCode":"\t\t// 3. Handle HTTP errors\n\t\tif (!response.ok) {\n\t\t\tlet errorData: any\n\t\t\ttry {\n\t\t\t\terrorData = await response.json()\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\t}\n\t\t\tconst errorMessage = errorData?.error?.message || response.statusText\n\n\t\t\tif (response.status === 401 || response.status === 403) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.AUTH_ERROR,\n\t\t\t\t\t`Authentication failed: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status === 429) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.RATE_LIMIT,\n\t\t\t\t\t`Rate limit exceeded: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.SERVER_ERROR,\n\t\t\t\t\t`Server error: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.UNKNOWN,\n\t\t\t\t`HTTP ${response.status}: ${errorMessage}`,\n\t\t\t\terrorData\n\t\t\t)\n\t\t}","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/alibaba/page-agent/blob/d02db1ee7c41f5315beda88bab2fe935c580f662/packages/llms/src/OpenAIClient.ts#L91-L127","documentation":"The endpoint returned HTTP 429 — you've exceeded a rate limit or quota (requests per minute, tokens per minute, or billing cap). The provider's error message and body are attached so you can see which limit was hit.","triggerScenarios":"invoke() bursts requests faster than the account/provider RPM/TPM allows; shared free-tier quota exhausted; concurrent agent loops firing many chat/completions calls; a specific model having a stricter tier limit.","commonSituations":"Agentic loops with no throttling; free-tier keys (strict RPM); retry storms amplifying traffic; billing quota exhausted; multiple environments sharing one key.","solutions":["Add exponential backoff retry on RATE_LIMIT errors (respect Retry-After header if present)","Reduce request frequency or concurrency in the calling loop","Check the provider dashboard for which limit (RPM/TPM/billing) is hit and request a raise or top up billing","Switch to a higher-limit model tier or distribute across keys/projects"],"exampleFix":"// before\nconst result = await llm.invoke(request)\n\n// after\nasync function invokeWithBackoff(llm, request, retries = 3) {\n  for (let i = 0; i <= retries; i++) {\n    try { return await llm.invoke(request) }\n    catch (e) {\n      if (e.code !== 'RATE_LIMIT' || i === retries) throw e\n      await new Promise(r => setTimeout(r, 2 ** i * 1000))\n    }\n  }\n}","handlingStrategy":"retry","validationCode":"// throttle before invoking: simple token bucket\nif (now - lastCallTime < minIntervalMs) await sleep(minIntervalMs - (now - lastCallTime))\nawait client.invoke(req)","typeGuard":"const isRateLimitError = (e: unknown): e is InvokeError =>\n  e instanceof InvokeError && e.code === 'RATE_LIMIT'","tryCatchPattern":"for (let attempt = 0; ; attempt++) {\n  try { return await client.invoke(req) }\n  catch (e) {\n    if (!isRateLimitError(e) || attempt >= 5) throw e\n    await sleep(2 ** attempt * 500)\n  }\n}","preventionTips":["Serialize or cap concurrency of agent loops that call invoke()","Cache identical completions within a session","Track RPM against your account tier and alert before hitting it"],"tags":["rate-limit","http-429","openai-client"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"d02db1ee7c41f5315beda88bab2fe935c580f662","analyzedAt":"2026-08-28T19:39:48.634Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}