moeru-ai/airi · error · Error

No audio tracks found in stream

Error message

No audio tracks found in stream

What it means

`getMediaStreamTrack(stream)` reads `stream.getAudioTracks()` and throws `No audio tracks found in stream` when the array is empty. `useAudioRecorder` relies on this to obtain the single track fed into the mediabunny WAV encoder; without a track there is nothing to record.

Source

Thrown at packages/stage-ui/src/composables/audio/audio-recorder.ts:15

import type { MaybeRefOrGetter } from 'vue'

import { until } from '@vueuse/core'
import { BufferTarget, MediaStreamAudioTrackSource, Output, QUALITY_MEDIUM, WavOutputFormat } from 'mediabunny'
import { computed, ref, shallowRef, toRef } from 'vue'

const TRANSCRIPTION_WAV_CODEC = 'pcm-s16'

/**
 * Returns the first audio track from the active microphone stream.
 */
function getMediaStreamTrack(stream: MediaStream) {
  const tracks = stream.getAudioTracks()
  if (!tracks.length)
    throw new Error('No audio tracks found in stream')
  return tracks[0]
}

/**
 * Records microphone input into short WAV blobs for transcription providers.
 */
export function useAudioRecorder(
  media: MaybeRefOrGetter<MediaStream | undefined>,
) {
  const mediaRef = toRef(media)
  const recording = shallowRef<Blob>()

  const mediaOutput = shallowRef<Output>()
  const mediaFormat = shallowRef<string>()
  const isRecording = computed(() => !!mediaOutput.value)

  const onStopRecordHooks = ref<Array<(recording: Blob | undefined) => Promise<void>>>([])

View on GitHub (pinned to 27111382b4)

Solutions

  1. Request audio explicitly: `getUserMedia({ audio: true })` and check the returned stream has audio tracks before recording.
  2. Guard the recorder start: bail or warn when `stream.getAudioTracks().length === 0`.
  3. Handle permission revocation by re-prompting the user and rebuilding the stream.
  4. Ensure no earlier code called `track.stop()` on the audio track.

Example fix

// before
const recorder = useAudioRecorder(stream)
await recorder.start()

// after
if (!stream || stream.getAudioTracks().length === 0)
  throw new Error('Microphone permission required; no audio track available')
const recorder = useAudioRecorder(stream)
await recorder.start()
Defensive patterns

Strategy: validation

Validate before calling

function hasAudioTrack(stream: MediaStream | undefined | null): stream is MediaStream {
  return !!stream && stream.getAudioTracks().length > 0
}

if (hasAudioTrack(stream)) useAudioRecorder(stream)

Type guard

function streamHasAudio(stream: MediaStream | undefined | null): stream is MediaStream {
  return !!stream && stream.getAudioTracks().length > 0
}

Try / catch

try {
  useAudioRecorder(stream)
} catch (e) {
  if (e instanceof Error && e.message === 'No audio tracks found in stream') {
    // re-prompt for microphone permission
  } else throw e
}

Prevention

When it happens

Trigger: The `MediaStream` passed to the recorder has no audio tracks — e.g. a stream obtained from `getUserMedia({ video: true })` with no `audio: true`, a display-capture stream with audio disabled, a stream whose audio track was already stopped/removed, or a stream from a device that failed permission.

Common situations: Permission denied/revoked mid-session so the audio track was dropped; `getUserMedia` constraints omitting `audio: true`; using a screen-share stream without `audio: true`; the microphone was unplugged after the stream started; browser autoplay policy blocking the track.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/220be1391b2d48f7. Report an issue: GitHub.