Skip to content

fix(love): clear stale ParticipantInfo on LiveKit participant_left / room_finished - #11030

Closed
nigad wants to merge 1 commit into
hcengineering:developfrom
nigad:fix/love-webhook-ghost-cleanup
Closed

nigad wants to merge 1 commit into
hcengineering:developfrom
nigad:fix/love-webhook-ghost-cleanup

Conversation

@nigad

@nigad nigad commented Sep 2, 2026

Copy link
Copy Markdown

Companion to #11029 (client side). This one fixes the state for everyone else, server side.

Description of the issue

ParticipantInfo (who is in which Love room) is written only by the person's own client, on join and on leave, and dropped by the server only when the person goes fully offline (OnUserStatus). When the client goes away mid-call — page reload, crash, laptop lid, network loss — LiveKit drops the participant, but the record keeps pointing at the room. The person stays listed as a member of the room for everyone, indefinitely, and nobody can remove them: the client offers Kick only to an office owner inside their own office.

The Love service already receives LiveKit webhooks (/webhook, used for egress_ended) and logs room_started / room_finished without acting on them.

Steps to reproduce

  1. Two users join a video room.
  2. One of them reloads the page (or loses network) mid-call.
  3. Observe the room from the other user's office view.

Expected behaviour

Once LiveKit has dropped the participant, the room no longer lists them.

Actual behaviour

They remain listed in the room until they go fully offline. On a self-hosted instance we saw a user pinned to a room this way for the rest of the day; LiveKit had logged participant closing … CLIENT_REQUEST_LEAVE at the moment of the reload.

Fix

  • services/love/src/rooms.ts: parseRoomName (workspace = first _ segment, room id = last; room names may contain underscores) and resetRoomParticipants(client, roomId, person?), which moves each matching ParticipantInfo to the person's office or to love.ids.Reception — the same update the client's kick() performs, so the existing OnParticipantInfo triggers keep RoomInfo / meeting minutes consistent.
  • WorkspaceClient: leaveRoom(person, roomId) and clearRoom(roomId) over the existing system-token TxOperations.
  • /webhook: participant_leftleaveRoom(identity, roomId) (the LiveKit identity is the Huly Person._id the client posts to /getToken); room_finishedclearRoom(roomId). Filtering by room makes a late participant_left harmless for someone who already moved to another room. Errors are logged and the webhook still returns 200 so LiveKit does not retry forever.

LiveKit only emits participant_left after the participant is fully gone (past its reconnect window), so no extra grace period is needed. Agent / bot identities have no ParticipantInfo and are a no-op. No new configuration: deployments that already receive egress webhooks get this for free.

Testing

Unit tests for parseRoomName and resetRoomParticipants (services/love/src/__tests__/rooms.test.ts): office target, reception fallback, person already elsewhere, whole-room reset. The reset write itself was verified on a self-hosted v0.7.432 instance through the transactor API (it is what freed the stuck user).

Developed with AI assistance; reviewed and tested by the author.

…room_finished

ParticipantInfo is written by the person's own client on join and leave
and dropped by the server only when the person goes fully offline. When
the client goes away mid-call (reload, crash, network loss) LiveKit drops
the participant but the record keeps pointing at the room: the person is
shown as a member indefinitely, cannot re-establish the call, and nobody
can remove them (Kick exists only for an office owner in their own office).

The Love service already receives LiveKit webhooks. Handle participant_left
by moving that person's record out of the room (to their office, else
reception) and room_finished by doing so for everyone still recorded in
it - the same update the client's own kick performs, so the server-side
room triggers apply as usual. Filtering by room keeps a late event
harmless for someone who has already moved on to another room. Failures
are logged and never fail the webhook.

Signed-off-by: Ayush Nigade <ayush@mirro.ai>
@nigad

nigad commented Sep 2, 2026

Copy link
Copy Markdown
Author

Closing; will be resubmitted from the correct account.

@nigad nigad closed this Sep 2, 2026
@nigad
nigad deleted the fix/love-webhook-ghost-cleanup branch September 2, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants