{"record":{"id":"ffbad726541ee0fa","repo":"different-ai/openwork","slug":"mcp-http-429","errorCode":"MCP_HTTP_429","errorMessage":"MCP_HTTP_429","messagePattern":"MCP_HTTP_429","errorType":"error_code","errorClass":null,"httpStatus":429,"severity":"warning","filePath":"ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts","lineNumber":929,"sourceCode":"      actionOwner: \"organization_admin\",\n      operatorAction: \"Verify the complete MCP endpoint path, including any provider tenant or instance prefix.\",\n    }\n  }\n  if ((status === 406 || status === 415) && phase.startsWith(\"MCP_\")) {\n    return {\n      phase: \"MCP_TRANSPORT\",\n      category: \"mcp_transport_negotiation\",\n      code: `MCP_HTTP_${status}`,\n      retryable: false,\n      actionOwner: \"provider_admin\",\n      operatorAction: \"Verify Streamable HTTP content negotiation and the provider's supported MCP transport.\",\n    }\n  }\n  if (status === 429) {\n    return {\n      phase,\n      category: \"provider_throttled\",\n      code: \"MCP_HTTP_429\",\n      retryable: true,\n      actionOwner: \"provider_admin\",\n      operatorAction: \"Wait for the provider rate limit to reset, then retry with bounded backoff.\",\n    }\n  }\n  if (status >= 500) {\n    return {\n      phase,\n      category: \"provider_unavailable\",\n      code: `MCP_HTTP_${status}`,\n      retryable: true,\n      actionOwner: \"provider_admin\",\n      operatorAction: \"Check provider availability and reverse-proxy logs using the diagnostic reference, then retry.\",\n    }\n  }\n  if ((status === 401 || status === 403) && phase.startsWith(\"MCP_\") && input.hasAuthorization) {\n    // A 401 is itself an authentication challenge. A 403 is ambiguous for\n    // enterprise providers: only a Bearer/insufficient_scope challenge means","sourceCodeStart":911,"sourceCodeEnd":947,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts#L911-L947","documentation":"MCP_HTTP_429 is raised when the external MCP provider responds with HTTP 429 Too Many Requests. The classifier marks it provider_throttled, retryable, and owned by the provider admin: the request is valid but the provider's rate limit has been exhausted. The phase is preserved from the failing request so operators can see which lifecycle stage was throttled.","triggerScenarios":"Any MCP-phase HTTP request (tool discovery, tool execution, initialize) that the provider answers with status 429. Typically triggered by high tool-call frequency, large parallel batches, or shared per-tenant provider quotas being consumed by other workloads.","commonSituations":"Automations or scheduled agents hammering a tool in a tight loop; several agents in the same org sharing one provider API quota; bursty catalog pagination during tool discovery tripping per-minute limits.","solutions":["Wait for the provider rate-limit window to reset, then retry with bounded exponential backoff.","Respect the Retry-After response header if the provider supplies one.","Throttle or queue MCP tool calls in the caller, and consider requesting a higher quota from the provider admin."],"exampleFix":"// before: immediate retry loop\nfor (;;) await callTool('search', args)\n// after: bounded backoff honoring Retry-After\nconst delay = Number(res.headers.get('Retry-After') ?? 30) * 1000\nawait sleep(delay)\nawait callTool('search', args)","handlingStrategy":"retry","validationCode":"// simple client-side rate limiter to stay under provider limits\nconst limiter = new TokenBucket({ ratePerMin: 50 })\nawait limiter.take(1) // before each MCP call","typeGuard":"function isThrottled(d: { code: string }): boolean {\n  return d.code === 'MCP_HTTP_429'\n}","tryCatchPattern":"for (let attempt = 0; attempt < 5; attempt++) {\n  try {\n    return await client.callTool(req)\n  } catch (e) {\n    if (!isThrottled(e.diagnostic)) throw e\n    const retryAfter = Number(e.headers?.['retry-after'] ?? 2 ** attempt)\n    await sleep(Math.min(retryAfter * 1000, 60_000)) // bounded backoff\n  }\n}\nthrow new Error('rate limit persisted after retries')","preventionTips":["Instrument call volume per provider and keep it under documented quota.","Use jittered exponential backoff with a max attempt count instead of tight loops.","Batch or queue scheduled automations rather than polling in parallel."],"tags":["mcp","rate-limit","http-429","throttling","retryable"],"backgroundTag":"rate-limit-429","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}