ghost register failure is not retried — transient 530 leaves a Discord speaker unbridged until next rejoin #134

Open
opened 2026-09-01 04:09:48 +00:00 by robocub · 0 comments
Collaborator

What happened

Prod, 2026-08-25 05:26:30 UTC ("Discord Bridge" bridge): a Discord speaker rejoined voice ~2s after leaving, and the ghost respawn died on the very first step:

ERROR ...:ghost{mxid=@nether-voicebridge_803521498823262229:nether.im}: nvb_matrix_rtc::rtc::ghost: ghost register failed — skipping error=POST /register failed: 530 <unknown status code>: error

HTTP 530 is a Cloudflare edge error (origin DNS blip) in front of nether.im — a one-shot transient, not a homeserver rejection.

Consequence

In crates/matrix-rtc/src/rtc/ghost.rs (register_ghost call at the top of the ghost span, ~line 789), any register failure is fatal for the spawn: log + return. Nothing re-attempts. Recovery only happens on the next presence edge (the Discord user leaving and rejoining voice).

Journal evidence of the gap: the user (803521498823262229) stayed in voice speaking (SSRC mapping logged at 05:26:33) until 05:29:18 — ~3 minutes present on Discord with no Matrix ghost, i.e. invisible and inaudible in the Element Call, with no signal to anyone that a participant was missing.

Why register deserves a retry

The later best-effort steps (displayname, avatar) already degrade gracefully, and join_room failure is a meaningful fatal gate (real 403s: room policy). But /register is idempotent (M_USER_IN_USE counts as success) and its failures are overwhelmingly transient edge blips — exactly the class that deserves a retry, not a skip.

Proposed fix

Retry register (and arguably join_room on 5xx-class errors too) with short backoff — e.g. 3–5 attempts over ~30s — while the assignment is still alive; abort only if the user already left (presence re-check after the await, same pattern as the #70 mint-after-leave fix). Keeps "ghost assigned" truthful instead of silently leaking a present-but-unbridged speaker.

Low frequency (one occurrence in 11 days of prod logs) but the failure mode is the bad kind: silent partial bridging.

## What happened Prod, 2026-08-25 05:26:30 UTC ("Discord Bridge" bridge): a Discord speaker rejoined voice ~2s after leaving, and the ghost respawn died on the very first step: ``` ERROR ...:ghost{mxid=@nether-voicebridge_803521498823262229:nether.im}: nvb_matrix_rtc::rtc::ghost: ghost register failed — skipping error=POST /register failed: 530 <unknown status code>: error ``` HTTP 530 is a Cloudflare edge error (origin DNS blip) in front of nether.im — a one-shot transient, not a homeserver rejection. ## Consequence In `crates/matrix-rtc/src/rtc/ghost.rs` (`register_ghost` call at the top of the ghost span, ~line 789), any register failure is fatal for the spawn: log + `return`. Nothing re-attempts. Recovery only happens on the next **presence edge** (the Discord user leaving and rejoining voice). Journal evidence of the gap: the user (`803521498823262229`) stayed in voice **speaking** (SSRC mapping logged at 05:26:33) until 05:29:18 — **~3 minutes present on Discord with no Matrix ghost**, i.e. invisible and inaudible in the Element Call, with no signal to anyone that a participant was missing. ## Why register deserves a retry The later best-effort steps (displayname, avatar) already degrade gracefully, and `join_room` failure is a *meaningful* fatal gate (real 403s: room policy). But `/register` is idempotent (`M_USER_IN_USE` counts as success) and its failures are overwhelmingly transient edge blips — exactly the class that deserves a retry, not a skip. ## Proposed fix Retry register (and arguably `join_room` on 5xx-class errors too) with short backoff — e.g. 3–5 attempts over ~30s — while the assignment is still alive; abort only if the user already left (presence re-check after the await, same pattern as the #70 mint-after-leave fix). Keeps "ghost assigned" truthful instead of silently leaking a present-but-unbridged speaker. Low frequency (one occurrence in 11 days of prod logs) but the failure mode is the bad kind: silent partial bridging.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
dark/nether-voicebridge#134
No description provided.