A user walks out of the office mid-call. Their phone drops the Wi-Fi and picks up 4G in about a second. The call then does one of two things: it freezes for a moment and carries on, or it ends with a "You've been disconnected" screen and a rejoin button.
Which one happens is not luck. A network change breaks three separate things, and each needs its own recovery. A good video SDK runs two of those recoveries for you. Video SDK reconnection fails most often in the parts it leaves to your code, and those parts are small, specific and easy to get wrong.
What a Network Change Actually Breaks
When the phone changes networks, it gets a new IP address. Everything bound to the old one stops working at the same moment, but at three different layers.
Layer | What breaks | What recovers it |
|---|---|---|
Media path | The ICE candidate pair carrying audio and video was tied to the old address | An ICE restart: gather new candidates, check pairs again |
Signalling | The WebSocket to the server dies with the old connection | Reconnect, then prove it is the same participant |
Session | The server's view of the participant: room, published tracks, subscriptions | Resume the existing session, or rebuild it from scratch |
The media path notices on its own clock. WebRTC keeps checking that the other side still wants its packets. RFC 7675 sets those consent checks at roughly every five seconds and lets consent expire after 30 seconds without an answer, at which point the browser must stop sending on that path. Getting it back takes an ICE restart or a new session.
Before that hard stop, the browser reports trouble through the ICE connection state. MDN's reference for iceConnectionState describes disconnected as a weaker signal than failed: it can appear on a shaky network and clear by itself. failed means every candidate pair was checked and none works. The timing between the two is left to each browser, so treat it as something to measure, not a constant.
None of this is new machinery. RFC 8445, the ICE specification, allows either side to restart ICE at any time, and the browser exposes it as restartIce(). If you want the background on how ICE finds a path in the first place, that post covers it. What matters here is what an SDK does when the path disappears.
Resume or Rejoin: The Two Recoveries an SDK Runs
Mature SDKs try a cheap recovery first and fall back to an expensive one. LiveKit's documentation is the clearest public description of this, so it is the example here.
The cheap path is a resume. According to LiveKit's connection guide, on a network change the SDK reconnects its signalling WebSocket and starts an ICE restart itself. The session survives. Users see nothing, or a video pause of a few seconds if media on the old path failed first.
The expensive path is a full reconnect, used when an ICE restart is not possible or does not work. The same guide describes the sequence: other participants are reported as disconnected, a Reconnecting event fires, the connection is rebuilt, Reconnected fires, every participant is announced as connected again, and local tracks are published again. LiveKit says plainly that this is the same as everyone leaving the room and coming back.
Twilio's SDK exposes the same split through error codes. Its reconnection documentation raises a reconnecting event with code 53001 for signalling and 53405 for media. If recovery gives up, the room disconnects with a reason:
Code | Meaning |
|---|---|
53000 | Signalling reconnection attempts exhausted |
53002 | Signalling reconnection took too long |
20104 | The access token expired |
The last row is the one most teams don't plan for, and it leads straight to the work an SDK cannot do for you.
The Three Jobs That Stay Yours
If you use an SDK, the first rule is to leave ICE restart alone. The SDK already watches the connection and restarts ICE on a network change. Adding your own restart from an iceconnectionstatechange handler duplicates work the SDK is already doing, and the two can collide. The quickstart-skips guide gives reconnection a paragraph; on an SDK, these three jobs are the rest of it.
Token lifetime
A reconnect has to prove the participant's identity again, usually with the token the client joined with. If that token has expired, recovery fails even though the network is fine. That is Twilio's 20104, and Twilio's answer is to issue tokens valid for the maximum session length of 24 hours.
That advice works against security, which wants tokens to expire quickly. LiveKit resolves the tension differently: its tokens and grants reference says the server issues refreshed tokens to connected clients, lasting ten minutes or the original token's remaining lifetime, whichever is longer, so expiry only constrains the first join. If your platform has no refresh, your client must fetch a fresh token from your server before the old one runs out. How to design short-lived tokens and the service that issues them is its own post; for reconnection, the rule is to pick a lifetime and confirm it survives a 30-minute call with a network change at minute 29.
Handlers that survive a replay
A full reconnect replays the room. Every "participant joined" and "track published" handler runs again for people who never left. Handlers that append without checking produce duplicate video tiles, double-counted attendance and two renderers fighting over one element. It looks like a rendering bug and is really a reconnect.
The fix is plain: key all room state by participant identity and track ID, and make every handler an upsert. Adding a participant who already exists should change nothing.
What the user sees
Users forgive a pause they can see explained. They don't forgive a frozen tile with no message. Three choices decide this:
- Don't drive the UI from one event. LiveKit lists Reconnecting only for the full-reconnect path, so a banner tied to it stays hidden during a resume. Its connection-quality value reaching "lost" arrives earlier.
- Don't tear down on disconnected. It is allowed to clear by itself. Show a "reconnecting" state after a short grace period instead.
- Tell the other side. Participants who stay put see a frozen face. A per-participant reconnecting indicator is cheap and saves the "can you hear me?" minute.
If You Are Building It Yourself
Without an SDK, all three layers are yours. The media layer is the familiar part:
pc.addEventListener('iceconnectionstatechange', () => {
if (pc.iceConnectionState === 'disconnected') {
graceTimer = setTimeout(() => pc.restartIce(), 3000);
}
if (pc.iceConnectionState === 'connected') {
clearTimeout(graceTimer);
}
if (pc.iceConnectionState === 'failed') {
pc.restartIce();
}
});
window.addEventListener('online', () => pc.restartIce());restartIce() makes the next offer an ICE restart, so your negotiation code has to handle negotiationneeded correctly, and the new offer travels over a signalling channel that may itself be reconnecting. That ordering is where most hand-built reconnection breaks: signalling must come back first, carrying a session ID the server recognises, before the restart offer can go anywhere.
The server needs a matching decision: how long it keeps a vanished participant's slot, published tracks and subscriptions before treating them as gone. No standard sets that window; it is a product decision. Too short and every tunnel becomes a rejoin. Too long and others watch a frozen tile.
Finally, the new network may be one that blocks UDP. A phone moving from home Wi-Fi onto a guest network can lose the path that worked at join time, so a restart that only tries UDP fails where a TCP or TLS relay on port 443 would have worked.
How to Know Whether It Works
There is no public data on how often calls survive a network change, and vendors don't publish their own rates. You have to measure yours.
Test the transitions users actually make: Wi-Fi to cellular, cellular to Wi-Fi, airplane mode for five seconds, airplane mode for 40 seconds (longer than the consent timeout), and a laptop that sleeps and wakes. For each, record which path ran (resume or full reconnect), how long media was gone, and whether the session came back as the same participant.
In production, the same questions come from the stats every session already produces: when bytes stopped arriving, when they resumed, and whether the candidate pair changed. Collecting getStats evidence in production shows which fields answer that and how to sample them. Count resumes and full reconnects separately. A rising full-reconnect share is the early warning.
The Bottom Line
A network change breaks the media path, the signalling channel and the session at once. An SDK recovers the first two and usually the third. What it cannot do is choose your token lifetime, make your event handlers idempotent or decide what users see, and those three decide whether a call survives the walk to the car park.
What's Next
Reconnection hurts most on mobile web. Next: iOS Safari, where the camera goes black without an error, even when the connection never dropped.
Frequently Asked Questions
How does video SDK reconnection work when a user switches from Wi-Fi to 4G?
The SDK reconnects its signalling connection and runs an ICE restart to find a new media path from the new IP address. If that works, the session resumes with at most a short video pause. If it fails, the SDK falls back to a full reconnect, which rebuilds the session as if everyone had left and rejoined.
What is the difference between ICE "disconnected" and "failed"?
disconnected means connectivity checks failed for at least one component and may clear on its own on a shaky network. failed means every candidate pair was checked and none works, which needs an ICE restart. Browsers decide the timing between them, so measure it rather than assume it.
Why does a call fail to reconnect even though the network is back?
Often the token expired. Reconnecting means proving the participant's identity again, and an expired token fails that check. Either issue tokens that outlive the session, or use a platform that refreshes tokens for connected clients.
Should I call restartIce() myself if I use a video SDK?
Usually not. Most SDKs restart ICE themselves on a network change, and a second restart from your code competes with theirs. Spend the effort on token lifetime, idempotent event handlers and the reconnecting UI instead.
How long should a server keep a disconnected participant's session?
There is no standard; it is a product decision. Too short turns every tunnel into a full rejoin, too long leaves others watching a frozen tile. When you evaluate a platform, Samvyo included, ask what the window is and whether you can tune it.
Does running video on my own infrastructure change how reconnection works?
The mechanics are the same: ICE restart, signalling resume and session state behave identically. What changes is that you own the TURN relays the new path may need. Samvyo keeps the media path and TURN on infrastructure you control, so relay capacity for reconnecting mobile users is something to size, not something a provider absorbs.