{"record":{"id":"2cf374c22551ce4d","repo":"moeru-ai/airi","slug":"no-microphone-stream-available-for-voice-input","errorCode":null,"errorMessage":"No microphone stream available for voice input","messagePattern":"No microphone stream available for voice input","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/composables/audio/voice-input-session.ts","lineNumber":565,"sourceCode":"          }\n        }\n\n        volumeFallbackAnimationFrame = requestAnimationFrame(analyze)\n      }\n\n      volumeFallbackAnimationFrame = requestAnimationFrame(analyze)\n    }\n    catch (error) {\n      stopVolumeFallback()\n      lastError.value = error\n      log('error', 'volume-fallback-start-failed', 'Failed to start volume-based recorder fallback.', { error })\n    }\n  }\n\n  async function startAutoSegmentation() {\n    const stream = mediaRef.value\n    if (!stream)\n      throw new Error('No microphone stream available for voice input')\n\n    await startVoiceInputVadDetectionSafely({\n      init: initVAD,\n      loaded: () => vadLoaded.value,\n      start: startVAD,\n      stream,\n      getError: () => vadError.value,\n      log,\n    })\n    await startVolumeFallback(stream)\n  }\n\n  async function stop(options: { flushActiveRecording?: boolean } = {}) {\n    stopVolumeFallback()\n    disposeVAD()\n    transcriptionChain.reset()\n    stoppedRecordingSegments.length = 0\n    vadRecordings.clear()","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/composables/audio/voice-input-session.ts#L547-L583","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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`.","solutions":["Ensure the microphone stream is acquired (`getUserMedia({ audio: true })`) and assigned to `mediaRef` before starting segmentation.","Gate the start button on a truthy stream (`watch(mediaRef, ...)` or `until(mediaRef).not.toBeNull()`).","Re-acquire the stream on permission/visibility change before restarting.","Surface a user-facing 'microphone not ready' state instead of throwing."],"exampleFix":"// before\nstartAutoSegmentation()  // mediaRef.value is undefined -> throws\n\n// after\nconst stream = mediaRef.value\nif (!stream) {\n  lastError.value = new Error('Microphone is not ready. Grant permission and try again.')\n  return\n}\nawait startAutoSegmentation()","handlingStrategy":"validation","validationCode":"if (!mediaRef.value) {\n  lastError.value = new Error('Microphone is not ready. Grant permission and try again.')\n  return\n}\nawait startAutoSegmentation()","typeGuard":"function hasMicrophoneStream(ref: { value: MediaStream | undefined }): boolean {\n  return !!ref.value && ref.value.getAudioTracks().length > 0\n}","tryCatchPattern":"try {\n  await startAutoSegmentation()\n} catch (e) {\n  if (e instanceof Error && e.message === 'No microphone stream available for voice input') {\n    // acquire stream, then retry\n  } else throw e\n}","preventionTips":["Acquire the microphone stream before exposing the start-listening action.","Gate the start button on a truthy mediaRef with audio tracks.","Re-acquire the stream on permission/visibility change."],"tags":["audio","voice-input","vad","mediastream","permissions"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}