usememos/memos · error
webm has no Opus audio track
Error message
webm has no Opus audio track
What it means
Returned by WebMOpusToWAV (internal/ai/audio) when the parsed WebM container's Segment.Tracks.TrackEntry contains no track with an Opus codec. The converter only supports the Opus audio tracks that browser MediaRecorder produces; a WebM with another codec (or video-only) cannot be converted to WAV for STT.
Source
Thrown at internal/ai/audio/webm.go:51
//
// The output is mono or stereo at 48 kHz (Opus's native decode rate),
// regardless of the original encoder's hint. Pre-skip samples declared in
// the OpusHead are discarded to avoid the encoder's startup padding.
//
// The function reads the entire WebM document into memory; callers should
// enforce their own size limits before invoking it.
func WebMOpusToWAV(input []byte) ([]byte, error) {
var doc struct {
Header webm.EBMLHeader `ebml:"EBML"`
Segment webm.Segment `ebml:"Segment"`
}
if err := ebml.Unmarshal(bytes.NewReader(input), &doc); err != nil && !errors.Is(err, io.EOF) {
return nil, errors.Wrap(err, "parse webm")
}
track := findOpusTrack(doc.Segment.Tracks.TrackEntry)
if track == nil {
return nil, errors.New("webm has no Opus audio track")
}
if len(track.CodecPrivate) < opusHeadMinLength {
return nil, errors.Errorf("invalid OpusHead: expected at least %d bytes, got %d", opusHeadMinLength, len(track.CodecPrivate))
}
channels := int(track.Audio.Channels)
if channels < 1 || channels > 2 {
return nil, errors.Errorf("unsupported Opus channel count: %d", channels)
}
preSkip := int(binary.LittleEndian.Uint16(track.CodecPrivate[10:12]))
decoder := opus.NewDecoder()
if err := decoder.Init(opusOutputSampleRate, channels); err != nil {
return nil, errors.Wrap(err, "init opus decoder")
}
pcm := make([]int16, 0, 1<<16)
frame := make([]int16, maxOpusPacketSamples*channels)View on GitHub (pinned to 14d757ce1f)
Solutions
- Record with an explicitly Opus mime type: MediaRecorder.isTypeSupported('audio/webm;codecs=opus') and use it.
- If the input is an uploaded file, validate/convert it server-side (ffmpeg -i in.webm -vn -c:a libopus out.webm) before this path.
- For video webm, strip to the audio track first; this converter never reads video tracks.
- Reject or transcode unsupported inputs upstream with a clear user-facing message.
Example fix
// before
const rec = new MediaRecorder(stream); // browser-default codec, may not be Opus
// after
const mime = ['audio/webm;codecs=opus', 'audio/webm'].find(m => MediaRecorder.isTypeSupported(m));
const rec = new MediaRecorder(stream, mime ? { mimeType: mime } : undefined); Defensive patterns
Strategy: validation
Validate before calling
// TypeScript — negotiate an Opus mime before recording
const mime = ["audio/webm;codecs=opus", "audio/webm"].find(m => MediaRecorder.isTypeSupported(m));
if (!mime) throw new Error("This browser cannot record Opus WebM audio");
const rec = new MediaRecorder(stream, { mimeType: mime }); Try / catch
// Go
wav, err := audio.WebMOpusToWAV(blob)
if err != nil {
if strings.Contains(err.Error(), "no Opus audio track") {
return status.Errorf(codes.InvalidArgument, "Audio must be WebM with an Opus track; re-record or convert the file")
}
return err
} Prevention
- Always set an explicit Opus mimeType on MediaRecorder when supported.
- Prefer audio/webm over video/webm recordings for voice input.
- If accepting uploads, convert with ffmpeg to Opus WebM (or WAV) before the STT path.
When it happens
Trigger: Feeding WebMOpusToWAV audio recorded with a MediaRecorder mimeType whose audio codec is not Opus (e.g., 'audio/webm;codecs=vorbis' where supported, or a video/webm recording with no audio track); uploading an arbitrary .webm file to the voice/STT path; a malformed file whose tracks parse but expose no Opus entry.
Common situations: Cross-browser differences in MediaRecorder codec support; users uploading non-recorded .webm files to the memo voice input; screen-capture webm (video-only, or VP8/VP9 video + no Opus audio); truncated recordings where the Tracks element lacks the audio entry.
Related errors
- OpenAI API key is required
- AI provider capability unsupported
- Internal
- SMTP host is required
- SMTP port must be between 1 and 65535
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/2719427bd17aa472.
Report an issue: GitHub.