RocketChat/Rocket.Chat · warning · Meteor.Error

too-many-requests

too-many-requests

Error message

DDPRateLimiter.getErrorMessage(rateLimitResult)

What it means

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.

Source

Thrown at apps/meteor/server/api/v1/misc.ts:666

			this.token ||
			crypto
				.createHash('sha256')
				.update((this.requestIp ?? '') + this.user._id)
				.digest('hex');

		const rateLimiterInput = {
			userId: this.userId,
			clientAddress: this.requestIp,
			type: 'method',
			name: method,
			connectionId,
		};

		try {
			DDPRateLimiter._increment(rateLimiterInput);
			const rateLimitResult = DDPRateLimiter._check(rateLimiterInput);
			if (!rateLimitResult.allowed) {
				throw new Meteor.Error('too-many-requests', DDPRateLimiter.getErrorMessage(rateLimitResult), {
					timeToReset: rateLimitResult.timeToReset,
				});
			}

			return API.v1.success(mountResult({ id, result: await Meteor.callAsync(method, ...params) }));
		} catch (err) {
			if (!(err as any).isClientSafe && !(err as any).meteorError) {
				SystemLogger.error({ msg: 'Exception while invoking method', err, method });
			}

			if (settings.get('Log_Level') === '2') {
				Meteor._debug(`Exception while invoking method ${method}`, err);
			}

			return API.v1.failure(mountResult({ id, error: err }));
		}
	},
);

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Reduce call frequency — implement exponential backoff and respect the timeToReset value in the error response.
  2. Check if the method has a custom rate limit rule via DDPRateLimiter and consider raising the limit if appropriate.
  3. Batch multiple operations into a single method call if the method supports it.
  4. Use the buffered variant (method.callBulk) to reduce individual request counts.

Example fix

// before: rapid polling
setInterval(() => callMethod('getUserStatus'), 100);
// after: respect rate limit
async function callWithBackoff(method) {
  try { return await api.callMethod(method); }
  catch (e) {
    if (e.error === 'too-many-requests') {
      const reset = e.details?.timeToReset || 5000;
      await sleep(reset);
      return callWithBackoff(method);
    }
    throw e;
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// Implement a token-bucket or fixed-window throttle client-side before calling method.call
const MIN_INTERVAL_MS = 200; // adjust based on known rate limits
let lastCall = 0;

async function throttledMethodCall(method, params) {
  const now = Date.now();
  const elapsed = now - lastCall;
  if (elapsed < MIN_INTERVAL_MS) {
    await new Promise(r => setTimeout(r, MIN_INTERVAL_MS - elapsed));
  }
  lastCall = Date.now();
  return callMethod(method, params);
}

Try / catch

try {
  return await callMethodOverRest(method, params);
} catch (e) {
  if (e.error === 'too-many-requests') {
    const timeToReset = e.details?.timeToReset ?? 5000;
    await new Promise(r => setTimeout(r, timeToReset));
    return callMethodOverRest(method, params); // retry once
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/030c3a6dfde1ad74. Report an issue: GitHub.