moeru-ai/airi · warning
3. Browser requires user gesture - ensure microphone was ena
Error message
3. Browser requires user gesture - ensure microphone was enabled by user action
What it means
Fourth line of the Web Speech API start-failure warn block: enumerated cause 3, 'Browser requires user gesture - ensure microphone was enabled by user action'. Some engines only allow SpeechRecognition.start() from a user-activation context; calling it from timers, autoplayed flows, or component mounts fails and startRecognition() returns false.
Source
Thrown at packages/stage-ui/src/libs/providers/providers/browser-web-speech-api/provider.ts:460
console.info('Web Speech API sound ended')
}
recognition.onaudioend = () => {
console.info('Web Speech API audio capture ended')
}
recognition.onnomatch = () => {
console.info('Web Speech API: No speech match')
}
const started = startRecognition()
if (!started) {
// If immediate start failed, it might be a permission issue
// Web Speech API will prompt for permission automatically, so we just log
console.warn('Web Speech API recognition did not start immediately. This might be due to:')
console.warn('1. Microphone permission not granted - browser should prompt automatically')
console.warn('2. Recognition already running - this is normal if called multiple times')
console.warn('3. Browser requires user gesture - ensure microphone was enabled by user action')
// Don't retry immediately - wait for permission or user action
// The recognition instance is already created, so it can be started later if needed
}
return {
fullStream,
text: deferredText.promise,
textStream,
recognition: recognitionInstance,
}
}
View on GitHub (pinned to 677329427f)
Solutions
- Bind the first recognition.start() to a click/tap handler that enables the feature
- Keep a user-activated 'microphone enabled' toggle; only auto-restart after the user has activated once in the session
- In tests, dispatch a real user event or grant and pre-warm the audio context before asserting recognition state
- If hands-free startup is required, prefer a provider that does not need a gesture (local whisper worker)
Example fix
// before: started from a timer, gesture-gated browsers refuse
setTimeout(() => startRecognition(), 100)
// after: first start from a user gesture, then auto-restart is allowed
enableMicButton.addEventListener('click', () => {
startRecognition()
}) Defensive patterns
Strategy: validation
Validate before calling
function hasUserActivation(): boolean {
return typeof navigator !== 'undefined' && Boolean(navigator.userActivation?.isActive)
}
if (!hasUserActivation())
await waitForUserClick(enableButton) Prevention
- Always begin speech sessions from a click or tap handler
- Persist an explicit 'microphone enabled by user' toggle before auto-restarting sessions
- Prefer gesture-free providers (local whisper worker) for hands-free startup flows
When it happens
Trigger: Starting recognition from setTimeout, an animation frame, a WebSocket event handler, or onMounted without prior user interaction; browsers (notably Safari and some mobile WebViews) that gate audio capture and speech services behind transient user activation.
Common situations: App auto-enables hearing on page load; voice features triggered by server push events; automated tests that call start() without synthesizing a gesture.
Related errors
- Web Speech API recognition did not start immediately. This m
- Web Speech API is not available in this environment. It requ
- Web Speech API error:
- Web Speech API: Microphone access issue. Please check microp
- Web Speech API failed to restart, creating new instance:
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/3ff525c4919c22aa.
Report an issue: GitHub.