{"record":{"id":"60297f6d8393403e","repo":"moeru-ai/airi","slug":"web-speech-api-recognition-did-not-start-immediate","errorCode":null,"errorMessage":"Web Speech API recognition did not start immediately. This might be due to:","messagePattern":"Web Speech API recognition did not start immediately\\. This might be due to:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/libs/providers/providers/browser-web-speech-api/provider.ts","lineNumber":457,"sourceCode":"  }\n\n  recognition.onsoundend = () => {\n    console.info('Web Speech API sound ended')\n  }\n\n  recognition.onaudioend = () => {\n    console.info('Web Speech API audio capture ended')\n  }\n\n  recognition.onnomatch = () => {\n    console.info('Web Speech API: No speech match')\n  }\n\n  const started = startRecognition()\n  if (!started) {\n    // If immediate start failed, it might be a permission issue\n    // Web Speech API will prompt for permission automatically, so we just log\n    console.warn('Web Speech API recognition did not start immediately. This might be due to:')\n    console.warn('1. Microphone permission not granted - browser should prompt automatically')\n    console.warn('2. Recognition already running - this is normal if called multiple times')\n    console.warn('3. Browser requires user gesture - ensure microphone was enabled by user action')\n\n    // Don't retry immediately - wait for permission or user action\n    // The recognition instance is already created, so it can be started later if needed\n  }\n\n  return {\n    fullStream,\n    text: deferredText.promise,\n    textStream,\n    recognition: recognitionInstance,\n  }\n}\n","sourceCodeStart":439,"sourceCodeEnd":473,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/libs/providers/providers/browser-web-speech-api/provider.ts#L439-L473","documentation":"First line of a multi-line console.warn block in the browser Web Speech API transcription provider. startRecognition() wraps recognition.start() and returns false when the call throws - most often an InvalidStateError (already started) or a permission/security failure. The provider deliberately does not retry: the SpeechRecognition instance stays created so a later start can succeed once microphone permission or a user gesture is available.","triggerScenarios":"Calling the provider's transcription entry while a SpeechRecognition instance is already running (double start), starting without microphone permission granted, starting outside a user gesture in browsers that require one, or running in a browser without window.SpeechRecognition / window.webkitSpeechRecognition. startRecognition() returns false and this warn block prints.","commonSituations":"First-run microphone prompt still pending; permission previously denied; Electron renderer without a user-gesture context; Chrome's speech service wedged after many restarts; iOS Safari gesture restrictions.","solutions":["Check the site's microphone permission and grant it, then trigger recognition again","Start recognition from a user interaction (button click) on first enable so gesture requirements are satisfied","Track running state via onstart/onend to avoid calling start() on an active recognition (InvalidStateError)","Confirm window.SpeechRecognition or window.webkitSpeechRecognition exists before selecting this provider (Firefox lacks it)","In Electron, allow 'media' via session.setPermissionRequestHandler so the renderer can use the API"],"exampleFix":"// before: recognition started automatically on mount\nstartRecognition()\n\n// after: gate the first start behind a user gesture, tolerate double start\nbutton.onclick = () => {\n  try {\n    recognition.start()\n  }\n  catch {\n    // InvalidStateError: recognition already running - safe to ignore\n  }\n}","handlingStrategy":"validation","validationCode":"const SR = window.SpeechRecognition ?? window.webkitSpeechRecognition\nif (!SR)\n  throw new Error('SpeechRecognition is not supported in this browser')\nconst perm = await navigator.permissions?.query({ name: 'microphone' as PermissionName })\nif (perm && perm.state === 'denied')\n  throw new Error('Microphone permission denied')","typeGuard":"function isSpeechRecognitionSupported(): boolean {\n  return typeof window !== 'undefined'\n    && Boolean(window.SpeechRecognition ?? window.webkitSpeechRecognition)\n}","tryCatchPattern":"recognition.onerror = (event) => {\n  if (event.error === 'not-allowed' || event.error === 'service-not-allowed')\n    promptForMicrophonePermission()\n}\ntry {\n  recognition.start()\n}\ncatch {\n  // InvalidStateError: already running - safe to ignore\n}","preventionTips":["Gate the first recognition.start() behind a user gesture","Track running state via onstart/onend to avoid double starts","Pre-check microphone permission with navigator.permissions.query","Offer a local whisper provider as fallback where Web Speech API is unavailable (Firefox)"],"tags":["web-speech-api","speech-recognition","microphone-permission","user-gesture"],"backgroundTag":"speech-recognition-start-failed","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}