WebRTC ICE Restart for Failover During AI Voice Calls (2026)
ICE restart is how an AI voice call survives Wi-Fi to cellular handoff without dropping the conversation. Here is the 2026 production pattern that keeps the agent alive.
If your AI voice call drops every time the user walks from the cafe Wi-Fi to the sidewalk, you are not running ICE restart. The fix takes ten lines of code and saves single-digit-percent of every cohort.
Why ICE restart matters
A WebRTC session is bound to a candidate pair. When the user's network changes — Wi-Fi to cellular, NAT binding expiring after 30 s of silence, TURN allocation timing out — the candidate pair is dead but the SCTP and DTLS state is fine. ICE restart re-runs candidate gathering, validates a new pair, and resumes media on the same logical connection. The user hears a 1–3 second freeze; the AI agent's session, the DataChannel, and any in-flight tool calls survive.
RFC 8445 defines the mechanic. The browser exposes it as `pc.restartIce()` (modern API) or `pc.createOffer({ iceRestart: true })` (older). Mobile users especially need it: a 2025 LiveKit field study found ~12% of mobile sessions experience at least one ICE-impacting network change in a 5-minute call.
Architecture pattern
```mermaid flowchart LR PC[RTCPeerConnection] -- iceConnectionState --> S{state?} S -- disconnected --> T[wait 3s] T -- still disconnected --> R[restartIce] R --> Renego[trigger renegotiation] S -- failed --> R ```
Two trigger conditions: 3-second `disconnected` (early intervention; the user has not noticed yet) and `failed` (catastrophic; restart immediately). The disconnected timer is critical — `failed` only fires after the full 30-second consent timeout, by which point most users have hung up.
CallSphere implementation
CallSphere wires ICE restart into every browser-side voice path:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
- /demo restarts on `disconnected` after 3 s. Median restart-to-resume on US cellular is 1.6 s.
- Real Estate (OneRoof, /industries/real-estate) uses ICE restart in tandem with our Pion Go gateway 1.23: the gateway is impolite (per the perfect-negotiation pattern) and re-issues the offer. NATS coordinates with the 6-container pod (CRM, MLS, calendar, SMS, audit, transcript) so any in-flight tool calls (CRM lookup, MLS query) get retried, not dropped.
- Healthcare uses the same pattern; HIPAA-relevant audit events note "session preserved across NAT change" rather than counting a new session.
Across 37 agents, 90+ tools, 115+ database tables, ICE restart is treated as a recovery primitive, not an error. SOC 2 logs a clean event for every restart. Pricing remains $149/$499/$1499 with the 14-day trial across all six verticals (real estate, healthcare, behavioral health, legal, salon, insurance); affiliates 22% — see /affiliate.
Code snippet
```ts const pc = new RTCPeerConnection({ iceServers }); let disconnectTimer: any;
pc.oniceconnectionstatechange = () => { switch (pc.iceConnectionState) { case "disconnected": disconnectTimer = setTimeout(() => { if (pc.iceConnectionState === "disconnected") { pc.restartIce(); } }, 3000); break; case "connected": case "completed": clearTimeout(disconnectTimer); break; case "failed": pc.restartIce(); break; } };
pc.onnegotiationneeded = async () => { await pc.setLocalDescription(); signaler.send({ description: pc.localDescription }); }; ```
Build steps
- Wire `oniceconnectionstatechange`; do not rely on `onconnectionstatechange` alone — they are not the same.
- Set a 3-second `disconnected` timer; do not wait for `failed` (30 s).
- Call `pc.restartIce()` and let `negotiationneeded` re-issue an offer naturally.
- Make sure your signalling channel is alive even when ICE is dead — typically a separate WebSocket.
- Coordinate with your AI session: pause the agent's response queue during restart, then resume.
- Track `iceRestartCount` per session; high counts indicate a sick TURN or routing issue.
- Add an exponential backoff on repeated restarts within 30 s — three failures means hand off to a "reconnecting" UI.
Common pitfalls
- Restarting on every `disconnected` — flicker on noisy networks causes thrash. Always include the 3-second guard.
- Forgetting the gateway side — the server-side peer must also handle the renegotiation; if it does not, the restart hangs in offer/answer limbo.
- Sticky TURN credentials — credentials minted at session start may have already expired; mint fresh ones before each restart.
- No fallback UI — users need a "reconnecting…" indicator at second 3 or they hang up.
- Counting restarts as new sessions — your session-volume metric will spike for no reason. Tag restarts as continuations.
FAQ
Does the user hear silence? Yes — typically 1–3 seconds. The DataChannel queues messages, so tool calls survive.
Does TURN need to support ICE restart? Yes. Coturn since 4.5 handles it; verify in your relay logs.
Will SFrame keys survive? Yes — keys are above the ICE layer.
What about server-side SFUs? Most SFUs (LiveKit, mediasoup, Janus) support restart since 2024.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Does Pion support it? Yes via `PeerConnection.RestartIce` since 3.x.
What if both sides are offline at once? A double-failure cannot recover; tear down and reconnect.
Does ICE restart work over a WebSocket signalling outage? Only if the signalling channel comes back; the restart needs to send a new offer.
Should I restart proactively? Some teams restart every 4 minutes preemptively to refresh NAT bindings. We have not seen the need with healthy TURN.
Production playbook for AI voice teams in 2026
Three rules from a year of mobile-heavy production:
- Restart count is a leading indicator. Sessions with >2 restarts in 5 minutes have 4x higher hangup rate. Surface the count in your support tooling so agents can see it.
- Plan for the AI agent's view. Tell the LLM "the user's connection blipped" rather than letting it hallucinate that the user said something. We add a system message at every restart.
- Test on a real device. Wi-Fi to LTE handoff cannot be simulated reliably. Buy three Android phones; rotate them through QA every release.
The hardest bug we shipped in 2025 was a perfect-negotiation interaction with restart that produced a 30 s deadlock on iOS Safari only on cellular. Caught only by physical-device QA. Worth the budget.
Watch list 2026
- iOS 18 background-tab restart behaviour changed quietly; calls that were suspended now restart with a fresh ICE pair. Make sure your code path handles it.
- Android Auto WebRTC uses a separate transport stack; ICE restart behaviour there is still being firmed up.
- Coturn 5.x lands a new restart-aware connection accounting; if you self-host, plan to upgrade.
- OpenAI Realtime SDK has built-in restart hints in 2026; if you use it, defer to the SDK.
The single biggest 2026 lesson on ICE restart for AI voice: it is not enough to restart silently. Modern users notice a 1.5 s freeze, and if you do not give them a UI cue ("Reconnecting…") they conclude the agent froze and hang up. We instrument every restart with both a server-side audit event and a client-side overlay; the overlay disappears the instant `iceConnectionState` returns to `connected`. That single UX detail moved our restart-survival rate from 71% to 94%.
Sources
- https://bloggeek.me/webrtcglossary/ice-restart/
- https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/restartIce
- https://datatracker.ietf.org/doc/html/rfc8445
- https://www.softpagecms.com/2026/01/06/why-webrtc-calls-fail-mobile-data-fix-2026/
- https://medium.com/@fippo/ice-restarts-5d759caceda6
- https://webrtc.github.io/samples/src/content/peerconnection/restart-ice/
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.