DEV Community

FlareCanary
FlareCanary

Posted on

OpenAI Realtime Beta Disappears May 7 — Your Voice Agent's Audio Handlers Will Stop Firing With No Error

On May 7, 2026 — five days from now — OpenAI removes the Realtime API beta. If you have a voice agent, transcription pipeline, or any WebSocket/WebRTC integration with gpt-4o-realtime-preview, you have a long weekend's worth of work to do, and most of it isn't the part the migration guide warns about.

The loud failures are easy. The WebSocket returns 401, the WebRTC connection won't establish, your session.update gets rejected. Those break in dev, you fix them, you ship.

The interesting failures — and the ones I keep writing this column about — are the silent ones. The connection works, the model responds, your tests pass, and audio just stops coming out of the speaker. Or text stops streaming. Or the voice changes. Or a function call output silently flips from text to audio. Code that was correct against the beta interface is now correct-shaped against an interface that's renamed half the events it emits.

This is the twelfth provider in the running tally and it's the same shape every time: the SDK still validates, the response still parses, the field your code depends on just isn't being sent under that name anymore.

The Silent Ones: Renamed Events

This is where most of the silent breakage lives. The Realtime API streams a lot of event types over the WebSocket — partial text deltas, audio chunks, transcript chunks. In the GA protocol, three of the most-listened-for events were renamed:

Beta name GA name
response.text.delta response.output_text.delta
response.audio.delta response.output_audio.delta
response.audio_transcript.delta response.output_audio_transcript.delta

If your client uses a typed event dispatcher — switch (msg.type), or a client.on("response.audio.delta", ...) style listener — the new event names just don't match. The handler isn't called. There's no error. The connection is healthy, the server is sending bytes, and your audio buffer never gets fed.

// Beta-era code, still compiles, still connects, plays no audio:
client.on("response.audio.delta", (event) => {
  audioBuffer.append(event.delta);
});

// In GA, this event is named response.output_audio.delta.
// The above handler will never fire. No exception, no warning,
// just silence on the speaker.
Enter fullscreen mode Exit fullscreen mode

The class of code that breaks here is "dispatch on event name." Every voice agent I've seen written against the beta has a switch or a listener registry keyed on these strings. They don't throw on unknown event names — that would be the right design — they silently no-op.

The tells are subtle. Tests that mock the WebSocket transport keep passing because the test fixtures still use the old names. Recorded interactions replay correctly. The first sign in production is a user saying "the bot just stopped talking to me," and your logs show a successful session with content streaming and no errors.

The Almost-Silent One: Content Type Renames

Inside the conversation item shape, two type tags were renamed:

  • type: "text"type: "output_text"
  • type: "audio"type: "output_audio"

Same failure mode. If you have rendering code that switches on content[i].type, it now hits the default branch — usually "render nothing" or "log unknown type and continue."

# Beta:
for part in item["content"]:
    if part["type"] == "text":
        render_text(part["text"])
    elif part["type"] == "audio":
        play_audio(part["audio"])
    # default: skip silently
Enter fullscreen mode Exit fullscreen mode

In GA, every assistant response part takes the default branch. The conversation appears to send empty turns. No exception. The model is responding correctly; your render layer just doesn't recognize the shape anymore.

This is identical in spirit to Stripe's Basil migration — the field you read still exists in the response, it's just spelled differently.

The Other Silent One: Restructured Session Config

session.update is the event you send to configure voice, transcription model, modalities, and so on. In the beta, much of this was flat. In GA, it nested into session.audio.input and session.audio.output. Voice selection, for example, moved from a top-level field to session.audio.output.voice.

If your client sends old-shape config to a GA endpoint, two things can happen depending on which fields you set:

  1. The server rejects the session.update (loud — easy to fix).
  2. The server accepts what it understands, ignores what it doesn't, and keeps defaults for the rest (silent — voice silently flips to the default alloy, transcription model silently falls back, etc.).

The mixed shape — some fields recognized, others ignored — is the worst case. The session is configured, just not the way you wrote it. Your "always use the verse voice for our brand" code now uses the default voice and nobody notices until QA listens to a recording.

The Loud Ones (For Completeness)

These break visibly. List them so you know what to expect during migration:

  1. OpenAI-Beta: realtime=v1 header. Remove it. Sending it against the GA endpoint causes auth/route confusion — you'll see 401 or 404 depending on path.
  2. api-version query parameter (Azure path). Strip it from the URL. The GA endpoint format is /openai/v1/realtime, no version suffix.
  3. session.update missing the new type field. Required in GA. Must be "realtime" for speech-to-speech or "transcription" for audio-only. Server returns an explicit error if absent.
  4. WebRTC ephemeral key endpoint. Was a session creation flow; is now POST /v1/realtime/client_secrets. Different request shape, different response shape. Old endpoint 404s.
  5. WebRTC connection URL. Now /v1/realtime/calls. Old browser SDP exchange path is gone.
  6. SDK version pins. OpenAI Python ≥ 1.54.0, JavaScript ≥ 4.77.0, .NET ≥ 2.9.0. Older SDKs hard-fail against GA.

The migration guide covers these well. The ones above this list are where it under-warns.

Why Tests Won't Catch It

Same pattern as the previous eleven providers in this series:

  • Mocked Realtime in tests. Your fixtures are recorded WebSocket transcripts from the beta. They still emit response.audio.delta. Tests pass; production silently breaks.
  • Schema validators don't help. zod / pydantic schemas that accept { type: string, ... } with a string-enum will pass output_audio as readily as audio. The typo isn't a schema violation, it's a meaning violation — the new value goes through a code path that expects different content.
  • Voice agents don't have unit tests. Most are tested by running them and listening. Silent audio is a noticeable bug, but only after a user reports it. Empty-text-stream renders look like the model "didn't say anything this turn" — easy to miss.
  • Logs look healthy. No exceptions, no 4xx, no warnings. The Realtime session metadata in the OpenAI dashboard shows successful turns. Every observability surface is green.

How to Detect This Class of Change

The general defense, which I keep restating because it keeps working, is to watch the shape of responses, not just the status code. For a streaming API like Realtime, that means:

  1. Log the set of event types your client receives in a session. A short script that subscribes to all events, hashes the names, and alerts when the hash changes. Catches every future event rename.
  2. Subscribe to a wildcard / unknown-event handler that logs (don't silently drop). If your event dispatcher has a default: branch, make it loud — console.warn("unhandled realtime event: " + msg.type). You'd have caught this rename on day one of the GA rollout in dev.
  3. Replay a representative session in CI against a non-production realtime endpoint. Capture the event-name set. Diff against last known good. This is what schema-drift monitoring does for REST APIs; the same idea applies to streaming protocols.
  4. Audit switch (msg.type) and event-listener registrations in your codebase. grep -r "response.text.delta\|response.audio.delta\|response.audio_transcript.delta" and update each call site. While you're there, add the wildcard branch.

For ongoing monitoring: do this for every streaming provider you depend on, not just OpenAI. The streaming-event-name failure mode is generalizable — Anthropic streams events, Google's voice APIs do, every voice/agent provider does, and they all rename events between major versions.

The Pattern, Now Twelve Months In

Twelfth provider in the running silent-breakage tally:

Provider Surface What Goes Wrong
Stripe Basil Subscription.current_period_end Moved to items[]; old reads return undefined
GitHub pull_request.merge_commit_sha Returns null on closed PRs in 2026-03-10 ver
GitHub Org security fields PATCH returns 200, applies nothing
OpenAI Responses input_text Rejected with Invalid value error
HubSpot Contacts v1 endpoints Return 200 with list-memberships silently dropped
Auth0 TLS handshake Weak ciphers start returning handshake_failure Jun 10
Twilio api.de1.twilio.com Removed; regional domains never actually routed regionally
Shopify Checkout metafields Returns undefined after 2026-04; orders ship without app data
Kubernetes 1.36 gitRepo volumes Pass validation, fail at deploy with FailedMount
Anthropic claude-3-haiku-20240307 Returns model-retired error after Apr 20
OpenAI DALL·E 2/3 Retired May 12; per-image billing flips to per-token
Exa /research + crawl-date filters + highlightScores 404, parameters silently ignored, fields null
OpenAI Realtime Audio/text/transcript event names Renamed; old listeners silently never fire

Thirteen providers, thirteen different shapes, one shared failure mode: the surface still answers, the SDK still validates, the thing your code reads or listens for is just not there under that name.

If you ship a voice agent that depends on the Realtime API, today is the day. May 7 is on a Thursday. The week after that, "the bot stopped talking" tickets are a much harder bug than "I renamed my event handlers on May 2 because the migration guide said to."

What I'm Building

I'm working on FlareCanary for exactly this class of bug. Point it at the API endpoints you depend on — REST, GraphQL, and now streaming — and it polls them on a schedule, learns the response shape and event vocabulary, and alerts when a field drops, a type flips, or an event renames. Free tier covers up to five endpoints, useful for keeping watch on your top external dependencies without building the monitor yourself.

You don't need a tool for this. You do need a habit. The Realtime GA rollout has been documented well enough that any team paying attention will catch it. The silent half-broken state still ships somewhere — to a team that pinned an old SDK, or to one that wrote a strict event listener six months ago and forgot.

That's the gap. HTTP 200 isn't enough. The connection succeeding isn't enough. The event names matter.


If your voice agent or transcription pipeline trips on the Realtime migration this week — or any other silent schema change — I'd like to hear about it. The "no error, just silence on the speaker" failures are exactly the ones I'm tracking. Drop a comment or reach out.

Top comments (0)