{"record":{"id":"030c3a6dfde1ad74","repo":"RocketChat/Rocket.Chat","slug":"too-many-requests","errorCode":"too-many-requests","errorMessage":"DDPRateLimiter.getErrorMessage(rateLimitResult)","messagePattern":"DDPRateLimiter\\.getErrorMessage\\(rateLimitResult\\)","errorType":"exception","errorClass":"Meteor.Error","httpStatus":429,"severity":"warning","filePath":"apps/meteor/server/api/v1/misc.ts","lineNumber":666,"sourceCode":"\t\t\tthis.token ||\n\t\t\tcrypto\n\t\t\t\t.createHash('sha256')\n\t\t\t\t.update((this.requestIp ?? '') + this.user._id)\n\t\t\t\t.digest('hex');\n\n\t\tconst rateLimiterInput = {\n\t\t\tuserId: this.userId,\n\t\t\tclientAddress: this.requestIp,\n\t\t\ttype: 'method',\n\t\t\tname: method,\n\t\t\tconnectionId,\n\t\t};\n\n\t\ttry {\n\t\t\tDDPRateLimiter._increment(rateLimiterInput);\n\t\t\tconst rateLimitResult = DDPRateLimiter._check(rateLimiterInput);\n\t\t\tif (!rateLimitResult.allowed) {\n\t\t\t\tthrow new Meteor.Error('too-many-requests', DDPRateLimiter.getErrorMessage(rateLimitResult), {\n\t\t\t\t\ttimeToReset: rateLimitResult.timeToReset,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn API.v1.success(mountResult({ id, result: await Meteor.callAsync(method, ...params) }));\n\t\t} catch (err) {\n\t\t\tif (!(err as any).isClientSafe && !(err as any).meteorError) {\n\t\t\t\tSystemLogger.error({ msg: 'Exception while invoking method', err, method });\n\t\t\t}\n\n\t\t\tif (settings.get('Log_Level') === '2') {\n\t\t\t\tMeteor._debug(`Exception while invoking method ${method}`, err);\n\t\t\t}\n\n\t\t\treturn API.v1.failure(mountResult({ id, error: err }));\n\t\t}\n\t},\n);","sourceCodeStart":648,"sourceCodeEnd":684,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/misc.ts#L648-L684","documentation":"Thrown by POST /api/v1/method.call/:method when DDPRateLimiter._check(rateLimiterInput) returns allowed: false. This endpoint proxies a Meteor method invocation over REST and applies DDP rate limiting per user, IP, method name, and connection ID. The error message is generated by DDPRateLimiter.getErrorMessage(rateLimitResult) and the timeToReset timestamp is included in the error metadata.","triggerScenarios":"Calling POST /api/v1/method.call/:method (authenticated) repeatedly fast enough to exceed the DDP rate limit rule for that specific method. The rate limit is keyed on userId, clientAddress, method name, and connectionId.","commonSituations":"A polling loop or retry mechanism calling the same method too rapidly; admin tightened the DDP rate limit rules for a specific method; multiple clients sharing a single NAT IP; a bot or integration making many sequential method calls without throttling.","solutions":["Reduce call frequency — implement exponential backoff and respect the timeToReset value in the error response.","Check if the method has a custom rate limit rule via DDPRateLimiter and consider raising the limit if appropriate.","Batch multiple operations into a single method call if the method supports it.","Use the buffered variant (method.callBulk) to reduce individual request counts."],"exampleFix":"// before: rapid polling\nsetInterval(() => callMethod('getUserStatus'), 100);\n// after: respect rate limit\nasync function callWithBackoff(method) {\n  try { return await api.callMethod(method); }\n  catch (e) {\n    if (e.error === 'too-many-requests') {\n      const reset = e.details?.timeToReset || 5000;\n      await sleep(reset);\n      return callWithBackoff(method);\n    }\n    throw e;\n  }\n}","handlingStrategy":"retry","validationCode":"// Implement a token-bucket or fixed-window throttle client-side before calling method.call\nconst MIN_INTERVAL_MS = 200; // adjust based on known rate limits\nlet lastCall = 0;\n\nasync function throttledMethodCall(method, params) {\n  const now = Date.now();\n  const elapsed = now - lastCall;\n  if (elapsed < MIN_INTERVAL_MS) {\n    await new Promise(r => setTimeout(r, MIN_INTERVAL_MS - elapsed));\n  }\n  lastCall = Date.now();\n  return callMethod(method, params);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await callMethodOverRest(method, params);\n} catch (e) {\n  if (e.error === 'too-many-requests') {\n    const timeToReset = e.details?.timeToReset ?? 5000;\n    await new Promise(r => setTimeout(r, timeToReset));\n    return callMethodOverRest(method, params); // retry once\n  }\n  throw e;\n}","preventionTips":["Throttle method.call requests client-side to stay under DDP rate limits.","Always read and respect the timeToReset value from the error response before retrying.","Use exponential backoff for retries rather than immediate re-submission.","Prefer WebSocket DDP connections over REST method.call for high-frequency method calls."],"tags":["rate-limiting","ddp","api","method-call"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}