Repository navigation
fix(api): accept .opus uploads on /v1/audio/transcriptions - #10607
Merged
diegosouzapw merged 1 commit intoAug 18, 2026
Merged
diegosouzapw merged 1 commit into
diegosouzapw merged 1 commit into
Conversation
Whisper-compatible upstreams pick the decoder from the multipart filename against an allow-list (flac, m4a, mp3, mp4, mpeg, mpga, oga, ogg, wav, webm) that has no `opus`, and OmniRoute forwarded the client's filename verbatim. The same bytes transcribed as `note.ogg` and 400'd as `note.opus`. Since /v1/audio/speech emits audio/opus for `response_format=opus`, clients re-uploading their own voice notes hit this on every round trip. A `.opus` file is Opus in an Ogg container (RFC 7845), so `.ogg` is a truthful relabel and is already on the allow-list. Rewrite the extension in getUploadedFileName, the single choke point feeding buildMultipartBody. The OpenRouter STT path had the same root cause with a quieter symptom: `.opus` matched neither its extension list nor its MIME map, so it fell through to the "wav" default and announced Opus bytes as WAV. Map both the extension and audio/opus to its already-supported ogg container. Fixes cheaper-inference#10588
Collaborator
|
Thanks for the clear write-up and the RFC 7845 rationale — this is a well-scoped fix. I re-ran your new test file locally (8/8 pass) and linted the three changed files (0 problems), so the code side is solid. The pending checks you're seeing are GitHub's first-time-contributor workflow-approval gate, not an actual failure (only semgrep-cloud-platform/scan has run so far, and it passed) — we'll approve those runs and get this merged once they're green. |
diegosouzapw
merged commit Aug 18, 2026
735d2c9
into
cheaper-inference:release/v3.8.50
3 checks passed
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…nference#10607) Whisper-compatible upstreams pick the decoder from the multipart filename against an allow-list (flac, m4a, mp3, mp4, mpeg, mpga, oga, ogg, wav, webm) that has no `opus`, and OmniRoute forwarded the client's filename verbatim. The same bytes transcribed as `note.ogg` and 400'd as `note.opus`. Since /v1/audio/speech emits audio/opus for `response_format=opus`, clients re-uploading their own voice notes hit this on every round trip. A `.opus` file is Opus in an Ogg container (RFC 7845), so `.ogg` is a truthful relabel and is already on the allow-list. Rewrite the extension in getUploadedFileName, the single choke point feeding buildMultipartBody. The OpenRouter STT path had the same root cause with a quieter symptom: `.opus` matched neither its extension list nor its MIME map, so it fell through to the "wav" default and announced Opus bytes as WAV. Map both the extension and audio/opus to its already-supported ogg container. Fixes cheaper-inference#10588
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #10588.
Problem
Whisper-compatible upstreams choose the decoder from the multipart filename, against an allow-list (
flac, m4a, mp3, mp4, mpeg, mpga, oga, ogg, wav, webm) that has noopus.getUploadedFileNameforwarded the client's filename verbatim, so byte-identical audio transcribed asnote.oggand 400'd asnote.opus.That closes a loop on this gateway:
/v1/audio/speechemitsaudio/opusforresponse_format=opus, so any client that saves its own output and re-uploads it — OpenClaw Talk, Telegram voice notes — fails every round trip.Fix
A
.opusfile is Opus audio in an Ogg container (RFC 7845), so.oggis a truthful relabel and is already on the allow-list. The rewrite goes ingetUploadedFileName, the single choke point that feedsbuildMultipartBody.While confirming the fix I found the same root cause in the OpenRouter STT path, with a quieter symptom.
resolveOpenRouterAudioFormatmatched.opusagainst neither its extension list nor its MIME map, so it fell through to the"wav"default — announcing Opus bytes to the upstream as WAV rather than erroring. Mapped both the extension andaudio/opusonto its already-supportedogg.Deliberately narrow
/\.opus$/i), soopus-demo.wavandtake.opus.mp3are untouched —opuselsewhere in a name is part of the name, not the container.audio.wavfallback for nameless blobs is unchanged..opusinresolveAudioContentType; it needed nothing.Content-Typealone. It still reflects the client'sfile.type, so anaudio/opusupload now sendsfilename="note.ogg"withContent-Type: audio/opus. Whisper validates on the filename, so this is enough to fix the 400 — say the word if you would rather normalize the MIME too.Tests
New file
tests/unit/audio-transcription-opus-filename.test.ts, 8 cases: the rename, case-insensitivity, the two anchoring guards, untouched extensions, the nameless-blob fallback, and three for the OpenRouter resolver..opuscases, and the four guard-rail tests pass either way, which is what they are for.audio-transcription-handler,audio-transcription-openrouter,multipart-body-arraybuffer-backing,kie-audio-transcription-result,speechmatics-transcription-provider— 41 pass, 0 fail including the new file.eslintclean on all three changed files.Per CONTRIBUTING I ran the focused loop rather than the full suite. One note for reproducing locally: a plain
npm installfails here on theopencode-aipostinstall (It seems your package manager failed to install the right opencode CLI package), unrelated to this change —npm install --ignore-scriptsgets you a working dev tree.