---
title: "WebRTC ICE Restart for Failover During AI Voice Calls (2026)"
description: "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."
canonical: https://callsphere.ai/blog/vw3e-webrtc-ice-restart-failover-ai-calls-2026
category: "AI Infrastructure"
tags: ["WebRTC", "ICE", "Failover", "Voice AI", "Mobile"]
author: "CallSphere Team"
published: 2026-03-31T00:00:00.000Z
updated: 2026-05-07T09:59:25.223Z
---

# 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:

- **/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](/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

1. Wire `oniceconnectionstatechange`; do not rely on `onconnectionstatechange` alone — they are not the same.
2. Set a 3-second `disconnected` timer; do not wait for `failed` (30 s).
3. Call `pc.restartIce()` and let `negotiationneeded` re-issue an offer naturally.
4. Make sure your signalling channel is alive even when ICE is dead — typically a separate WebSocket.
5. Coordinate with your AI session: pause the agent's response queue during restart, then resume.
6. Track `iceRestartCount` per session; high counts indicate a sick TURN or routing issue.
7. 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.

**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:

1. **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.
2. **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.
3. **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://bloggeek.me/webrtcglossary/ice-restart/)
- [https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/restartIce](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/restartIce)
- [https://datatracker.ietf.org/doc/html/rfc8445](https://datatracker.ietf.org/doc/html/rfc8445)
- [https://www.softpagecms.com/2026/01/06/why-webrtc-calls-fail-mobile-data-fix-2026/](https://www.softpagecms.com/2026/01/06/why-webrtc-calls-fail-mobile-data-fix-2026/)
- [https://medium.com/@fippo/ice-restarts-5d759caceda6](https://medium.com/@fippo/ice-restarts-5d759caceda6)
- [https://webrtc.github.io/samples/src/content/peerconnection/restart-ice/](https://webrtc.github.io/samples/src/content/peerconnection/restart-ice/)

Test resilience on [/demo](/demo) or start a [/trial](/trial).

---

Source: https://callsphere.ai/blog/vw3e-webrtc-ice-restart-failover-ai-calls-2026
