moeru-ai/airi · warning

No audio stream available

Error message

No audio stream available

What it means

Second guard in setupAudioMonitoring(): after the device check passes, the hearing store's startStream() is awaited and stream.value verified. If no MediaStream materialized - getUserMedia denied, no audio hardware, insecure context, or the store failed to acquire the mic - the function warns and returns false, so monitoring never starts.

Source

Thrown at packages/stage-pages/src/pages/settings/modules/hearing.vue:154

  if (useVADModel.value && loadedVAD.value) {
    return isSpeechVAD.value
  }

  return isSpeechVolume.value
})

async function setupAudioMonitoring() {
  try {
    if (!selectedAudioInput.value) {
      console.warn('No audio input device selected')
      return false
    }

    await stopAudioMonitoring()

    await startStream()
    if (!stream.value) {
      console.warn('No audio stream available')
      return false
    }

    if (supportsStreamInput.value) {
      // The Hearing pipeline owns speech segmentation and the provider session.
      // The page VAD below only drives the visualization for streaming providers.
      await transcribeForMediaStream(stream.value, {
        consumerId: hearingPlaygroundTranscriptionConsumerId,
        onSpeechEnd: finishStreaming,
        onTranscriptionUpdate: replaceStreamingText,
      })
    }

    const source = audioContext.value.createMediaStreamSource(stream.value)

    // Fallback speaking detection (when VAD model is not used)
    const analyzer = startAnalyzer(audioContext.value)
    onAnalyzerUpdate((volumeLevel) => {

View on GitHub (pinned to 677329427f)

Solutions

  1. Grant microphone permission for the origin and retry setup
  2. Serve over HTTPS or localhost - getUserMedia requires a secure context
  3. Verify a microphone exists and is enabled at the OS level
  4. Avoid racing setup with stopAudioMonitoring; serialize start/stop toggles
  5. Look for the preceding console error from startStream/getUserMedia - the DOMException name (NotAllowedError, NotFoundError, NotReadableError) identifies the cause

Example fix

// before
await startStream()
if (!stream.value) {
  console.warn('No audio stream available')
  return false
}

// after - surface the real acquisition failure and validate tracks
try {
  await startStream()
}
catch (err) {
  console.error('getUserMedia failed:', (err as DOMException).name)
  return false
}
if (!stream.value || stream.value.getAudioTracks().length === 0)
  return false
Defensive patterns

Strategy: validation

Validate before calling

if (!window.isSecureContext)
  throw new Error('Microphone access requires HTTPS or localhost')
const perm = await navigator.permissions.query({ name: 'microphone' as PermissionName })
if (perm.state === 'denied')
  throw new Error('Microphone permission denied')
await startStream()
if (!stream.value || stream.value.getAudioTracks().length === 0)
  return false

Try / catch

try {
  await startStream()
}
catch (err) {
  const name = (err as DOMException).name
  if (name === 'NotAllowedError') showPermissionUI()
  else if (name === 'NotFoundError') showNoDeviceUI()
  else throw err
  return false
}

Prevention

When it happens

Trigger: getUserMedia rejects (NotAllowedError after denial, NotFoundError without hardware); page served over plain http (not localhost) where getUserMedia is unavailable; stream acquired but immediately torn down by a concurrent stopAudioMonitoring; OS-level exclusive mic lock by another application.

Common situations: Permission denied then retry; dev server accessed over http on a non-localhost host; VMs without audio devices; race between toggling monitoring off and on rapidly.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/5ca1ce2f1521cf0b. Report an issue: GitHub.