Status save failed with "server may be rate limiting" — Synapse allows one presence write per 10 s per user #226

Closed
opened 2026-09-19 17:43:31 -04:00 by jared · 0 comments
Owner

Reported by the owner: changing the status message showed "Failed to save status — server may be rate limiting. Try again." on the first change in a month.

Root cause (verified against Synapse 1.161 source): PUT /_matrix/client/v3/presence/{user}/status is rate-limited by rc_presence.per_user, whose default is per_second: 0.1, burst_count: 1one presence write per 10 seconds per user, shared across all of that user's devices — and the servlet answers 429 M_LIMIT_EXCEEDED (with retry_after_ms). Our presence heartbeat (usePresenceUpdater: online/away on visibility and activity changes, from every open tab and the desktop app) spends that budget, so a manual save that lands within 10 s of any heartbeat was refused, and the form treated the 429 as a dead end.

Fixed in f5e7fb47:

  1. The status save waits out Retry-After (bounded ~25 s) instead of failing; the error text now says what actually happened.
  2. The heartbeat dedupes — it only writes when presence or status actually changes (/sync already keeps us online), so it stops consuming the budget.

Reproduced headless with a routed 10 s limiter: heartbeat ok → save 429 → retried 8 s later → saved.

Optional server-side relief (matrix repo / Synapse homeserver.yaml on the host): the default is very tight for a chat with presence badges; rc_presence: {{ per_user: {{ per_second: 1, burst_count: 5 }} }} would let a status change and a heartbeat coexist without the client having to wait. Not required with the client fix.

Reported by the owner: changing the status message showed *"Failed to save status — server may be rate limiting. Try again."* on the first change in a month. **Root cause (verified against Synapse 1.161 source):** `PUT /_matrix/client/v3/presence/{user}/status` is rate-limited by `rc_presence.per_user`, whose default is `per_second: 0.1, burst_count: 1` — **one presence write per 10 seconds per user, shared across all of that user's devices** — and the servlet answers `429 M_LIMIT_EXCEEDED` (with `retry_after_ms`). Our presence heartbeat (`usePresenceUpdater`: online/away on visibility and activity changes, from every open tab and the desktop app) spends that budget, so a manual save that lands within 10 s of any heartbeat was refused, and the form treated the 429 as a dead end. **Fixed in `f5e7fb47`:** 1. The status save waits out `Retry-After` (bounded ~25 s) instead of failing; the error text now says what actually happened. 2. The heartbeat dedupes — it only writes when presence or status actually changes (`/sync` already keeps us online), so it stops consuming the budget. Reproduced headless with a routed 10 s limiter: heartbeat ok → save 429 → retried 8 s later → saved. **Optional server-side relief** (matrix repo / Synapse `homeserver.yaml` on the host): the default is very tight for a chat with presence badges; `rc_presence: {{ per_user: {{ per_second: 1, burst_count: 5 }} }}` would let a status change and a heartbeat coexist without the client having to wait. Not required with the client fix.
jared added the bugpriority: mediumarea: settings labels 2026-09-19 17:43:31 -04:00
jared closed this issue 2026-09-19 17:43:31 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#226