OtterMind/Chat2DB · warning · RequestError
rate limit exceeded
Error message
rate limit exceeded
What it means
Raised at relay_server.py:264 when RateLimiter.acquire() returns False: more than config.rate_limit (default 30) accepted deliveries have started within the last config.rate_window_seconds (default 60). The limiter is a sliding window (relay_server.py:83) shared across all clients. Returned as HTTP 429. Note the delivery slot is released on this path (relay_server.py:286), so the delivery_id is free to retry.
Source
Thrown at script/github/qq_relay/relay_server.py:264
delivery_id, message = self._validate_payload(self._read_payload())
existing = self.relay_state.deliveries.reserve(delivery_id)
if existing is not None:
if existing == "pending":
raise RequestError(
HTTPStatus.SERVICE_UNAVAILABLE, "delivery is still in progress"
)
self._send_json(
HTTPStatus.OK,
{
"ok": True,
"duplicate": True,
"message_id": existing,
},
)
return
reserved = True
if not self.relay_state.rate_limiter.acquire():
raise RequestError(HTTPStatus.TOO_MANY_REQUESTS, "rate limit exceeded")
url_removed = False
try:
message_id = self.relay_state.onebot.send_group_message(message)
except OneBotRejected:
fallback_message = _remove_urls(message)
if fallback_message == message:
raise
message_id = self.relay_state.onebot.send_group_message(fallback_message)
url_removed = True
self.relay_state.deliveries.complete(delivery_id, message_id)
reserved = False
self._send_json(
HTTPStatus.OK,
{
"ok": True,
"duplicate": False,
"message_id": message_id,
"url_removed": url_removed,View on GitHub (pinned to 5ee1e990e7)
Solutions
- Back off and retry the same delivery_id after the window; respect the 429.
- Raise RELAY_RATE_LIMIT on the relay to match the expected burst size.
- Throttle/coalesce events on the sender so they arrive below the cap.
Example fix
# before
resp = requests.post(url, json=payload)
# after
resp = requests.post(url, json=payload)
if resp.status_code == 429:
time.sleep(60) # wait out the default 60s window
resp = requests.post(url, json=payload) # retry same delivery_id Defensive patterns
Strategy: retry
Validate before calling
# client-side throttle to stay under the relay's window
import time
rate_limit = int(os.environ.get('RELAY_RATE_LIMIT', '30'))
min_interval = 60 / rate_limit
time.sleep(max(0.0, min_interval - (time.monotonic() - last_send))) Try / catch
resp = requests.post(url, json=payload)
if resp.status_code == 429:
retry_after = int(resp.headers.get('Retry-After', '60'))
time.sleep(retry_after)
resp = requests.post(url, json=payload) # same delivery_id Prevention
- Throttle the sender to stay below RELAY_RATE_LIMIT per minute.
- Retry with the same delivery_id; the slot is released on the 429 path.
When it happens
Trigger: A burst of GitHub events within the window exceeds the cap; many repos or many events forwarded through one relay; a flood from a large PR or CI run.
Common situations: Default 30/min is too low for an active repo and RELAY_RATE_LIMIT was not raised; sender has no client-side throttling and floods on reconnect.
Related errors
- delivery is still in progress
- request body must be a JSON object
- repository is not allowed
- delivery_id is invalid
- message must be non-empty text
AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14).
Data as JSON: /api/errors/246fb1d0a9168319.
Report an issue: GitHub.