moeru-ai/airi · error · Error

No microphone stream available for voice input

Error message

No microphone stream available for voice input

What it means

`startAutoSegmentation` reads `mediaRef.value` (the microphone `MediaStream`) and throws `No microphone stream available for voice input` when it is falsy. The VAD-based segmentation and the volume fallback both require a live stream to attach analyser/source nodes.

Source

Thrown at packages/stage-ui/src/composables/audio/voice-input-session.ts:565

          }
        }

        volumeFallbackAnimationFrame = requestAnimationFrame(analyze)
      }

      volumeFallbackAnimationFrame = requestAnimationFrame(analyze)
    }
    catch (error) {
      stopVolumeFallback()
      lastError.value = error
      log('error', 'volume-fallback-start-failed', 'Failed to start volume-based recorder fallback.', { error })
    }
  }

  async function startAutoSegmentation() {
    const stream = mediaRef.value
    if (!stream)
      throw new Error('No microphone stream available for voice input')

    await startVoiceInputVadDetectionSafely({
      init: initVAD,
      loaded: () => vadLoaded.value,
      start: startVAD,
      stream,
      getError: () => vadError.value,
      log,
    })
    await startVolumeFallback(stream)
  }

  async function stop(options: { flushActiveRecording?: boolean } = {}) {
    stopVolumeFallback()
    disposeVAD()
    transcriptionChain.reset()
    stoppedRecordingSegments.length = 0
    vadRecordings.clear()

View on GitHub (pinned to 27111382b4)

Solutions

  1. Ensure the microphone stream is acquired (`getUserMedia({ audio: true })`) and assigned to `mediaRef` before starting segmentation.
  2. Gate the start button on a truthy stream (`watch(mediaRef, ...)` or `until(mediaRef).not.toBeNull()`).
  3. Re-acquire the stream on permission/visibility change before restarting.
  4. Surface a user-facing 'microphone not ready' state instead of throwing.

Example fix

// before
startAutoSegmentation()  // mediaRef.value is undefined -> throws

// after
const stream = mediaRef.value
if (!stream) {
  lastError.value = new Error('Microphone is not ready. Grant permission and try again.')
  return
}
await startAutoSegmentation()
Defensive patterns

Strategy: validation

Validate before calling

if (!mediaRef.value) {
  lastError.value = new Error('Microphone is not ready. Grant permission and try again.')
  return
}
await startAutoSegmentation()

Type guard

function hasMicrophoneStream(ref: { value: MediaStream | undefined }): boolean {
  return !!ref.value && ref.value.getAudioTracks().length > 0
}

Try / catch

try {
  await startAutoSegmentation()
} catch (e) {
  if (e instanceof Error && e.message === 'No microphone stream available for voice input') {
    // acquire stream, then retry
  } else throw e
}

Prevention

When it happens

Trigger: Starting auto voice segmentation before `mediaRef` has been populated (e.g. before `getUserMedia` resolved, after the stream was stopped/revoked, or when the parent never assigned a stream). The check is a synchronous read of the ref's current value.

Common situations: Calling `startAutoSegmentation` before microphone permission was granted; the stream was cleared on tab visibility change or device change; race where the UI 'start listening' action fires before the stream composable emits; ref not wired to the actual `MediaStream`.

Related errors


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