Firefox + Realtime API (WebRTC): voice sessions drop deterministically on the user's second speech turn

Summary

Realtime API sessions via WebRTC drop deterministically on the user’s second speech turn (~30–90s elapsed depending on the duration of the AI’s first response), only on Firefox. Same code, same OpenAI account, same model (gpt-realtime-1.5) works end-to-end on Chrome and Edge through 5+ minute sessions including multiple session.update events.

The drop fires on input_audio_buffer.speech_started, manifests as iceConnectionState: disconnectedconnectionState: disconnected → data channel close. No application-level error event ever fires from the server before the drop — only rate_limits.updated, then the closing connection-state events.

The Realtime Playground works in Firefox, but I confirmed via DevTools and about:webrtc that the Playground uses WebSocket transport. So the Playground sidesteps the bug rather than disproving it — this isolates the issue to the WebRTC path specifically, not to anything Firefox-vs-OpenAI more broadly.

UPDATE (2026-05-08): Root cause diagnosed by Mozilla. BUG 2037009

OpenAI’s Realtime media server requires the USE-CANDIDATE STUN attribute
in ICE consent-refresh checks. This is not spec-compliant — USE-CANDIDATE
is only defined for ICE connectivity checks, not consent checks (RFC 7675
§ 5.1). Firefox correctly omits it; Chrome and Safari incorrectly include
it, which coincidentally makes the consent checks valid from your
server’s perspective.

Confirmed by Byron Campen (Mozilla WebRTC engineer) via a local Firefox
patch that adds USE-CANDIDATE to consent checks — sessions then run
without dropping. Your fix: **ignore USE-CANDIDATE in consent checks** on
the media server. Only check it on the initial ICE connectivity checks.

Affected users beyond me confirming in this thread and on
the Bugzilla bug (Victor, comment 3). Firefox is ~3% of desktop browser
share, non-trivial number of dropped sessions.

Environment

  • Firefox version: [150.0.1]

  • OS: [Windows]

  • Model: gpt-realtime-1.5

  • Reproduction rate: 100% across 10+ sessions, multiple networks

Symptom (console trace)

[rt-event] response.done                       ← AI finishes turn 1
[rt-event] output_audio_buffer.stopped
[rt-event] input_audio_buffer.speech_started   ← user starts turn 2
[rt-ice-state] disconnected                    ← drop, 5–10s later
[rt-conn-state] disconnected
[rt-dc-close] readyState: closed

about:webrtc shows several successful ICE consent-refresh exchanges followed by STUN-CLIENT(consent): Timed out. Connection terminates before any application-layer error is signalled.

Reproduction

  1. Browser-side WebRTC connection to Realtime API on Firefox

  2. Standard session.update (full payload below)

  3. AI greets, user speaks turn 1 (works), AI responds (works)

  4. User starts speaking turn 2 → drop within 5–10 seconds

Reproduces across two networks (WiFi + mobile hotspot), Firefox normal mode + private mode, and Firefox on mobile.

Reference session.update payload

json

{
  "type": "session.update",
  "session": {
    "type": "realtime",
    "instructions": "<character role-play prompt>",
    "audio": {
      "input": {
        "turn_detection": { "type": "semantic_vad" },
        "transcription": { "model": "gpt-4o-mini-transcribe", "language": "en" }
      },
      "output": { "voice": "ash" }
    }
  }
}

Hypotheses tested and ruled out

I built URL-param diagnostic toggles for each hypothesis and tested them in isolation. All ruled out:

Hypothesis Test Result
VAD type Swap semantic_vadserver_vad with default thresholds Still drops
System prompt size (~35K chars) Replace with ~500-char slim prompt Still drops
language hint on transcription Drop "language": "en" entirely Still drops
Combined Playground-likely config server_vad + slim prompt + no language hint Still drops
NAT / STUN traversal Add explicit STUN + public TURN; UDP-direct candidate pair confirmed via pc.getStats() Still drops
Trust-update mid-session Disable mid-session session.update entirely Still drops
Client-side noise gate (AudioContext + GainNode) Bypass, feed raw mic stream into pc.addTrack Still drops
Browser audio processing getUserMedia({ audio: { echoCancellation: false, noiseSuppression: false, autoGainControl: false } }) Still drops
Output GainNode (AudioContext on incoming track) Bypass, native <audio> playback Still drops
Whisper-1 transcription model Swap to gpt-4o-mini-transcribe Still drops

What works

  • Chrome (latest): full sessions through 5+ minutes, multiple session.update events, no drops

  • Edge (latest): validated end-to-end through full app pipeline (voice → debrief → scoring)

  • Realtime Playground in Firefox: works end-to-end (uses WebSocket transport, not WebRTC)

What I’d love OpenAI to investigate

  • Anything specific to Firefox’s mtransport WebRTC stack vs. Chrome’s libwebrtc interacting with the Realtime media server

  • Whether the server-side STUN consent-refresh handling assumes Chromium-style timing (Firefox’s consent-refresh is stricter — 5s timeout)

  • Whether the GA endpoint applies an implicit default config (e.g., input_audio_noise_reduction) that interacts poorly with Firefox’s WebRTC stack. Prior forum threads document similar “Firefox WebRTC suddenly fails, Chrome works, no app-level error” patterns — different surface symptoms but plausibly related

Same problem here. Was working a few days ago. Now after a few seconds, 2-3 turns, the connection failed with a “WebRTC: ICE failed, add a STUN server and see about:webrtc for more details” error in firefox console.
In Chrome everything is working fine, Firefox has also worked in the past. Tested in different networks and OS.

We’re seeing this too. Same app/session flow works reliably in Chrome, but Firefox drops the Realtime WebRTC voice session deterministically.

Pattern from our logs:

- The model finishes speaking normally.

- We receive response.done, then rate_limits.updated.

- For ~5-10s after that, Firefox still reports RTCPeerConnection/data channel as connected/open.

- bytesReceived stops increasing, while bytesSent continues.

- Then ICE flips to disconnected and the data channel closes.

- Our backend sideband connection to OpenAI also gets websocket close 1006 / unexpected EOF around the same time.

- We are not intentionally closing the PeerConnection or data channel.

- Chrome does not reproduce this with the same backend/proxy/session config.

The strongest signal for us is that OpenAI/media responses appear to stop before Firefox reports ICE disconnected, so the browser disconnect looks downstream rather than app-initiated.

I am facing the same error.

Hi and welcome to the community!

Thank you for raising this!
I have reached out to the team to take a look and investigate.

Thanks for the detailed repro notes. This does look isolated to the Firefox WebRTC path rather than VAD, transcription, prompt size, or normal Realtime session behavior, especially since the same flow works in Chrome/Edge and the Playground path you checked is WebSocket rather than WebRTC.

I’d treat gpt-realtime-2 as a useful comparison test, not a guaranteed fix. If your model ID is easy to swap, could you try the same Firefox WebRTC flow with gpt-realtime-2 and confirm whether the second-turn ICE disconnect still happens?

For now, the practical workaround is Chrome/Edge, or a WebSocket-based path if Firefox support is required immediately. If it still reproduces on gpt-realtime-2, the most useful next detail would be the failing model ID plus one rough timestamp/timezone from a failed session. Thanks!

Hi Prashant,

I re-ran the same repro page in Firefox 150 with the model swapped to gpt-realtime-2. The ICE disconnect still reproduces, same failure shape.

  • Failing model ID: gpt-realtime-2

  • Failure timestamp: 2026-05-11T12:56:38.554+02:00 (CEST, Warsaw)

  • Pattern: Connection established at 12:55:57.665. Six user turns completed. ICE state went to disconnected at 12:56:38.554 — 13.2s after the final input_audio_buffer.speech_started (12:56:25.335), and 9.1s into the AI’s response (output_audio_buffer.started at 12:56:29.491). response.done never fired before the drop. conn-state went failed at 12:57:01.484.

  • Same flow on Firefox 150, Win64. Note that the original “second user turn” framing in the Bugzilla report was the typical timing — here the connection survived several quick exchanges before dying on a substantive turn followed by a longer AI reply.

So the bug is consistent across both gpt-realtime-1.5 and gpt-realtime-2. We’re steering users to Chrome/Edge for now.

A few highlights from the dump:

  • Selected nominated pair was peer-reflexive over CGNAT: 10.130.119.97:38696/UDP → 74.248.148.7:3478/UDP. Local host IP is 192.168.1.67, so the path goes through an intermediate NAT (hotel WiFi).

  • Connection survived 6 successful consent refreshes, then STUN-CLIENT(consent): Timed out fired 5 times in a row, ending with Consent refresh final time out — same mechanism as the original Bugzilla report.

  • Side observations from the ICE log that may or may not be relevant: (ice/ERR) duplicate priority 1853817087 candidate prflx and candidate prflx fires multiple times during pairing; and the second BUNDLE’d stream (mid:1, datachannel) shows peer has no stream matching stream transport-id=transport_1 warnings, even though the same SDP works in Chrome.

Best,
Omer

Hey @omerboomer , Can you please share the request id for these requests. Request ID is returned in the response headers as x-request-id for the connection/request. If you’re using a client or script, please log the response headers from the failing Realtime API call and share that x-request-id with us so we can trace it on our side.

Hi Prashant — the x-request-id response header doesn’t appear to be exposed to JavaScript via CORS on this endpoint, and Firefox DevTools’ Network panel isn’t capturing the cross-origin POST for some reason. Here’s the precise timestamp anchor for the failing request instead:

  • POST https://api.openai.com/v1/realtime/calls?model=gpt-realtime-2

  • Sent: 2026-05-12T09:00:26.078+02:00 (CEST, Warsaw)

  • Response received: 09:00:27.595 (status 201)

  • ICE disconnect fired at: 09:01:07.934 (~40 seconds later, 8.3s after response.created)

  • Same Firefox 150 / Win64 / gpt-realtime-2 flow.

  • Earlier failing repro for reference: 2026-05-11T12:55:55.603+02:00 (POST sent) → ICE disconnect at 12:56:38.554.

Let me know if there’s another way to capture the request ID on your end, or if I should adjust the repro to log it differently. Thanks.

Thanks, the timestamp anchors are useful. I checked around that window, but timestamps alone still leave a few nearby Realtime WebRTC sessions, so the best identifier now would be the Realtime call/session id from the 201 response body if your repro page can log it. It should look like `rtc_...`.

Apologies again for back and forth but this will help us immensely so we can identify the root cause and resolve this for you. Thank yoU!

We’re seeing the exact same issue. Sessions always drop in Firefox, but work fine in Safari and Chrome. It doesn’t always happen on the second speech turn, but in all our cases it drops within the first minute of the conversation. One Session ID where the drop occurred (I hope that’s the ID you’re asking for): rtc_u0_DensPXCLRPoWm4tBWFw34

I’m also getting same problem:

Firefox 151.0 (64-bit)

OS: Windows 11 Home 25H2 (OS Build 26200.8457)

I get to have a 2 or 3 turn conversation before connection goes bad.


POST https://api.openai.com/v1/realtime/calls?model=gpt-realtime-mini

11:27:20.106 [OpenAI sdpResponse] timestamp: 2026-05-21T16:27:20.106Z

11:27:20.106 [OpenAI sdpResponse] status: 201

11:27:20.107 [OpenAI sdpResponse] location: /v1/realtime/calls/rtc_u2_Di0WqTkPrkf50OZidR1uP

11:27:20.107 [OpenAI sdpResponse] x-request-id: null
All exposed headers:
11:27:20.107 cf-ray: 9ff4f293fec52fb2-BOG
11:27:20.108 content-length: 1541
11:27:20.108 content-type: text/plain; charset=utf-8
11:27:20.108 location: /v1/realtime/calls/rtc_u2_Di0WqTkPrkf50OZidR1uP

11:28:24.134 WebRTC: ICE failed, add a TURN server and see about:webrtc for more details


POST https://api.openai.com/v1/realtime/calls?model=gpt-realtime-2

11:42:01.125 [OpenAI sdpResponse] timestamp: 2026-05-21T16:42:01.125Z

11:42:01.127 [OpenAI sdpResponse] status: 201

11:42:01.128 [OpenAI sdpResponse] location: /v1/realtime/calls/rtc_u1_Di0l3ydcF5A4QRm2tGEcD

11:42:01.128 [OpenAI sdpResponse] x-request-id: null

All exposed headers:

                  11:42:01.129 cf-ray: 9ff508158a04da19-BOG 

                  11:42:01.129 content-length: 1541 

                  11:42:01.130 content-type: text/plain; charset=utf-8 

                  11:42:01.130 location: /v1/realtime/calls/rtc_u1_Di0l3ydcF5A4QRm2tGEcD 

11:42:58.615 WebRTC: ICE failed, add a TURN server and see about:webrtc for more details

Hi,

Important update: re-ran the repro just now on Firefox 151.0. The bug did not reproduce in this run, connection survived ~1m45s through ~14 substantive turns and an AI response, no ICE disconnect, manual disconnect at end.

Going to run this some more times to confirm the change is consistent and not a one-off.