moeru-ai/airi · warning

2. Recognition already running - this is normal if called mu

Error message

2. Recognition already running - this is normal if called multiple times

What it means

Third line of the Web Speech API start-failure warn block: enumerated cause 2, 'Recognition already running - this is normal if called multiple times'. recognition.start() on an instance in the 'listening' state throws InvalidStateError, which startRecognition() converts to a false return. The log marks this as expected noise rather than a fault.

Source

Thrown at packages/stage-ui/src/libs/providers/providers/browser-web-speech-api/provider.ts:459

  recognition.onsoundend = () => {
    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

  1. Track a running flag set in recognition.onstart and cleared in recognition.onend; only call start() when it is false
  2. Call recognition.abort() before re-starting if you intentionally want a fresh session
  3. Debounce or serialize start requests from UI toggles
  4. Treat this specific occurrence as benign - transcription continues from the already-running instance

Example fix

// before
function begin() {
  try { recognition.start() } catch { /* double start throws */ }
}

// after
const running = ref(false)
recognition.onstart = () => { running.value = true }
recognition.onend = () => { running.value = false }
function begin() {
  if (!running.value) recognition.start()
}
Defensive patterns

Strategy: validation

Validate before calling

const running = ref(false)
recognition.onstart = () => { running.value = true }
recognition.onend = () => { running.value = false }
if (!running.value)
  startRecognition()

Try / catch

try {
  recognition.start()
}
catch (err) {
  if ((err as DOMException).name === 'InvalidStateError')
    return // already running
  throw err
}

Prevention

When it happens

Trigger: Calling the provider twice without an intervening onend, re-invoking transcription while a previous session is still active, or restart logic that fires before the previous recognition fully stopped (between start and the onend event).

Common situations: Toggle handlers that do not debounce; auto-restart timers overlapping a still-running recognition; React/Vue strict-mode double effects in dev starting recognition twice.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/0ea090e7985bd381. Report an issue: GitHub.