Skip to content

fix(openai): drop ?model= on native /realtime STT URL to avoid invalid_model#1767

Open
tsushanth wants to merge 1 commit into
livekit:mainfrom
tsushanth:fix/openai-realtime-stt-invalid-model
Open

fix(openai): drop ?model= on native /realtime STT URL to avoid invalid_model#1767
tsushanth wants to merge 1 commit into
livekit:mainfrom
tsushanth:fix/openai-realtime-stt-invalid-model

Conversation

@tsushanth

Copy link
Copy Markdown

Summary

Realtime STT through the OpenAI plugin currently fails on every transcription model when connecting directly to wss://api.openai.com/.../realtime:

{"type":"error","error":{"type":"invalid_request_error","code":"invalid_model",
 "message":"Model \"gpt-4o-mini-transcribe\" is not supported in transcription mode."}}

…followed by close 4000 reason=invalid_request_error.invalid_model. So with useRealtime: true (the default for realtime-capable models), the plugin produces zero transcripts against the native endpoint.

Root cause

buildRealtimeSttUrl appends ?model=… to the WebSocket upgrade URL. As of 2026-06, OpenAI's native /realtime endpoint treats a ?model= query param as selecting a conversation session; the subsequent transcription-mode session.update is then rejected for any transcription model.

Verified directly against wss://api.openai.com/v1/realtime?intent=transcription:

  • Without &model=...: identical session.updatesession.updated, and audio streams transcribe correctly (deltas + completed events)
  • With &model=...: invalid_model for every model tried (gpt-4o-mini-transcribe, gpt-4o-transcribe, whisper-1)

The model is conveyed in the documented place for transcription sessions — session.update → audio.input.transcription.model — so the URL param is redundant for OpenAI direct.

Why not just delete ?model=?

The param was added in #1467 specifically for OpenAI-compatible proxies (LiteLLM, Cloudflare AI Gateway, etc.) that route WebSocket connections at the HTTP upgrade and need to know the model before any JSON frame arrives. Deleting it across the board would regress those proxy users for STT.

Fix

Skip the model param only when the host is api.openai.com. Non-OpenAI proxy users still receive the model on the URL so routing keeps working.

   url.searchParams.set('intent', 'transcription');
-  url.searchParams.set('model', model);
+  if (url.hostname !== 'api.openai.com') {
+    url.searchParams.set('model', model);
+  }
   return url.toString();

Updated the JSDoc to reflect the new endpoint behaviour, updated the existing URL builder test that asserted model was present on the native endpoint, and added a second test covering an explicit baseURL that still points at api.openai.com.

Test plan

  • pnpm vitest run plugins/openai/src/stt.test.ts — 12 passed, 1 skipped (the pre-existing integration test gated on OPENAI_API_KEY)
  • pnpm build:agents + pnpm build:plugins — clean
  • pnpm lint — no new warnings on touched files
  • pnpm format:check — clean
  • Changeset added (patch)
  • Proxy users keep the ?model= on the upgrade URL — https://gateway.example.com/v1, http://gateway.example.com/v1, and wss://gateway.example.com/v1/realtime test cases all still assert model is set

Closes #1756

…d_model

OpenAI's native wss://api.openai.com/.../realtime endpoint now treats a
?model= query param on the WebSocket upgrade URL as selecting a
conversation session, and rejects the subsequent transcription-mode
session.update with error.invalid_request_error.invalid_model (close
4000). Every transcription model (gpt-4o-mini-transcribe,
gpt-4o-transcribe, whisper-1) currently fails this way against the
native endpoint, so realtime STT through this plugin produces zero
transcripts.

The ?model= upgrade-URL convention exists for OpenAI-compatible proxies
(LiteLLM, Cloudflare AI Gateway, etc.) that route at the HTTP upgrade
without parsing the first JSON frame — see livekit#1467. Drop the param only
when the host is api.openai.com; non-OpenAI hosts still receive the
model on the URL so proxy routing keeps working. The model is conveyed
to OpenAI via session.update → audio.input.transcription.model instead.

Updates the corresponding URL builder test and adds a second test for
the explicit-OpenAI-baseURL path.

Closes livekit#1756
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0c547bc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 35 packages
Name Type
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-xai Patch
@livekit/agents Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugins-test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@CLAassistant

CLAassistant commented Jun 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openai: realtime STT (transcription session) rejects every model with invalid_model — caused by the model URL query param

2 participants