{"record":{"id":"7e6e2cfca354b2c8","repo":"moeru-ai/airi","slug":"2-recognition-already-running-this-is-normal-if-7e6e2c","errorCode":null,"errorMessage":"2. Recognition already running - this is normal if called multiple times","messagePattern":"2\\. Recognition already running - this is normal if called multiple times","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/provider-inference/src/providers/local/browser-web-speech-api/provider.ts","lineNumber":459,"sourceCode":"  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":441,"sourceCodeEnd":473,"githubUrl":"https://github.com/moeru-ai/airi/blob/f679616c34f1cf6d282c8d64264242af944b3fed/packages/provider-inference/src/providers/local/browser-web-speech-api/provider.ts#L441-L473","documentation":"Line 3 of the multi-line diagnostic `console.warn` block in `streamWebSpeechAPITranscription`, emitted when recognition did not start immediately. This line notes that the recognition instance may already be running, which is normal when the transcription function is called multiple times without stopping the previous session. It is informational; the provider's start handler explicitly treats 'already started' errors as OK and returns true.","triggerScenarios":"Calling `streamWebSpeechAPITranscription` (or `recognition.start()`) while a previous `SpeechRecognition` instance is still active; rapid re-invocation of `result` without awaiting stream completion or calling `stop()`.","commonSituations":"Double-clicking the mic button; re-entering a voice UI while a session is live; component remount creating a second recognizer; hot module reload leaving an orphan recognition instance.","solutions":["Track the active recognition instance and call `stop()` (or `abort()`) before starting a new session.","Guard the start with an `isListening` flag so repeated invocations are ignored while a session is live.","Debounce or disable the mic button while transcription is in progress.","The provider already tolerates 'already started' — if you see this warning with working audio, it can safely be ignored."],"exampleFix":"// before\nmicButton.onclick = () => streamWebSpeechAPITranscription(...)\n\n// after\nlet activeSession = null\nmicButton.onclick = async () => {\n  if (activeSession) return\n  activeSession = await streamWebSpeechAPITranscription(...)\n  activeSession.text.finally(() => { activeSession = null })\n}","handlingStrategy":"validation","validationCode":"let isListening = false\nasync function safeStart() {\n  if (isListening) return\n  isListening = true\n  try { await startTranscription() } finally { /* reset in onend/onerror */ }\n}","typeGuard":"function isRecognitionActive(instance: SpeechRecognition | null): instance is SpeechRecognition { return instance !== null }","tryCatchPattern":"// not-throwing path: guard start calls instead\nif (isListening) return existingSession\ntry {\n  session = await streamWebSpeechAPITranscription(...)\n} finally {\n  session.text.finally(() => { isListening = false })\n}","preventionTips":["Keep a single recognizer session and gate starts behind an isListening flag.","Call stop()/abort() in cleanup (component unmount, onend, onerror) before any restart.","Disable the mic toggle while a session is live to prevent double starts.","Treat this warning as benign when audio still flows — the provider tolerates 'already started'."],"tags":["web-speech-api","microphone","browser","diagnostic"],"backgroundTag":"speech-recognition-start-failed","analyzedSha":"f679616c34f1cf6d282c8d64264242af944b3fed","analyzedAt":"2026-09-08T13:18:15.005Z","contentChangedAt":"2026-09-08T13:18:15.005Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}