Description
Duplicate participant (ghost user) is created when reconnecting to a session after a network disconnection of 1.5 to 2 minutes.
This issue occurs on Web Video SDK version 2.4.5, whereas on version 2.2.12 with the exact same application codebase and reconnection logic, the SDK successfully resumes/rebinds the session without spawning a duplicate user.
Browser Console Error
No explicit JS console error is thrown. However, when calling zmClient.join() upon network recovery, the SDK triggers the user-added event for the same user identity, resulting in two participants with the same name/identity in zmClient.getAllUser().
Which Web Video SDK version?
Issue occurs on: 2.4.5
Works fine (no duplicate user) on: 2.2.12
Video SDK Code Snippets
N/A
To Reproduce(If applicable)
Join a Zoom Video SDK session as a participant (e.g., “Host”).
Disconnect the network interface completely (simulating internet loss) for 1 minute 30 seconds to 2 minutes.
Re-enable the network interface.
The client application detects internet restoration and calls zmClient.join() using the same credentials.
See duplicate “Host” user in the meeting list (One active with AV, one inactive ghost user).
At exactly 3 minutes since the initial network drop (step 2), the ghost user is removed automatically via the user-removed event.
Screenshots (Please attach the screenshot of your UI showing 2 “host” users here)
Troubleshooting Routes
N/A
Device (please complete the following information):
Device: Windows PC
OS: Windows 10/11
Browser: Google Chrome
Browser Version: [e.g. 120.0.0.0]
Additional context
We noticed major WebAssembly changes in the SDK lib directory between version 2.2.12 and 2.4.5 (such as the addition of audio.dynamic.simd.wasm and new voice translation types). Did the session resumption / auto-rebind behavior change under the hood when calling zmClient.join()?
What is the recommended approach in SDK 2.4.5 to reconnect a dropped user without creating a duplicate participant before the server-side keep-alive timeout (3 minutes) expires?
After connection-change: Reconnecting, the SDK itself attempts reconnection “for a few minutes.” Applications should observe that state, not issue another join(). A participant in failover is exposed through isInFailover and remains temporarily in the participant list before removal. Zoom event-handling documentation
You don’t have to call join a second time. The SDK would automatically attempt to reconnect in a few seconds when connection is restored.
Thank you for the clear explanation and recommendation! We will update our client-side logic to rely entirely on the SDK’s automatic reconnection mechanism and avoid calling join() a second time.
However, we would like to clarify a behavioral difference we observed between SDK versions:
In SDK version 2.2.12: Calling zmClient.join() upon network restoration with the same credentials did not result in duplicate/ghost participants. The session was rebound or replaced cleanly.
In SDK version 2.4.5: The exact same manual join logic creates a duplicate participant (“ghost user”) in the participant list for up to 3 minutes until the failover timeout kicks in.
Could you clarify if this is an intentional change in the SDK/backend behavior starting from newer versions (e.g., the backend no longer replaces/kicks the previous session when a new join request with the same user identity is received)?
The ghost user will remain in the session. This occurs because the original user has lost the socket connection to the server.
During reconnection, if the network connection is completely lost, the SDK cannot retrieve the session information. It then reports the connection-change state as Fail and clears the session-related token. Calling the join method afterward creates a new user, which is why a ghost user appears in the session.
If the user is experiencing a weak or unstable network connection, the SDK will continue attempting to reconnect. In that case, the session-related token has not yet been cleared, so calling the join method will reuse the existing session token and no ghost user will appear in the session.
In 2.4.5, how can we manually rejoin after a network loss without creating a ghost user? Should we call leave() or destroy the client first?
Calling leave() or destroy() clears any residual session tokens, which is the reason the ghost user appears as described above. During the version updates from 2.2.12 to 2.4.5, we fixed several session-join issues and improved the cleanup mechanism, which accounts for the different behavior.
If you do not want ghost users to appear, you can simply filter out users where user.isInFailover is true when displaying the participant list.