moeru-ai/airi · error · Error

Web Speech API is not available in this environment. It…

Error message

Web Speech API is not available in this environment. It requires a browser context with SpeechRecognition support (Chrome, Edge, Safari).

What it means

createWebSpeechAPIProvider runs an environment check at construction: it needs a window object exposing SpeechRecognition or webkitSpeechRecognition. The throw fires when the provider is instantiated in a non-browser context or in a browser without the API — Firefox is the notable gap among major browsers. Construction alone is enough; no transcription call is needed.

Solutions

  1. Feature-detect before constructing — only call createWebSpeechAPIProvider when the constructor exists
  2. Construct the provider lazily at selection time instead of at module or list load
  3. In Firefox, steer users to a server-side transcription provider
  4. Run unit tests for this provider only in files with a DOM test environment, or skip them

Example fix

// before
const providers = [createWebSpeechAPIProvider(), ...others] // throws in Node/SSR/Firefox

// after
const hasWebSpeech = typeof window !== 'undefined'
  && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)
const providers = [
  ...(hasWebSpeech ? [createWebSpeechAPIProvider()] : []),
  ...others,
]
Defensive patterns

Strategy: validation

Validate before calling

const speechAvailable = typeof window !== 'undefined'
  && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)
if (!speechAvailable)
  hideProvider('browser-web-speech-api')

Type guard

function getSpeechRecognitionCtor(): (new () => SpeechRecognition) | null {
  if (typeof window === 'undefined') return null
  return (window as any).SpeechRecognition ?? (window as any).webkitSpeechRecognition ?? null
}

Try / catch

try {
  const provider = createWebSpeechAPIProvider()
}
catch (err) {
  if (err.message.includes('Web Speech API'))
    selectFallbackTranscriptionProvider()
}

Prevention

When it happens

Trigger: Importing and constructing the provider during SSR, in Node-based unit tests, or in an Electron main process; running the renderer in Firefox; an older Safari build that only ships the webkit-prefixed constructor.

Common situations: Test files transitively import the provider module and Node lacks window; a provider list page instantiates every provider unconditionally; Firefox users select browser speech recognition.

Related errors


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

Appendix: source

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

/**
 * Web Speech API Speech Recognition provider
 *
 * This is a free, browser-native STT solution that requires no API keys.
 * Available in Chrome, Edge, Safari, and other Chromium-based browsers.
 *
 * Limitations:
 * - Only works in browser contexts (Electron renderer, web browsers)
 * - Requires user permission for microphone access
 * - Language support depends on browser implementation
 * - Not available in Node.js or Tauri main process
 */
export function createWebSpeechAPIProvider(): TranscriptionProviderWithExtraOptions<string, WebSpeechAPIExtraOptions> {
  // Check if Web Speech API is available
  const isAvailable = typeof window !== 'undefined'
    && ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)

  if (!isAvailable) {
    throw new Error('Web Speech API is not available in this environment. It requires a browser context with SpeechRecognition support (Chrome, Edge, Safari).')
  }

  const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition

  return {
    transcription: (model: string, extraOptions?: WebSpeechAPIExtraOptions) => {
      return {
        baseURL: 'about:blank', // Web Speech API doesn't use HTTP endpoints
        model: model || 'web-speech-api',
        fetch: async (_request: RequestInfo | URL, _init?: RequestInit) => {
          // Web Speech API does not support file-based transcription - it only supports live streaming
          // Check if a file is provided in the request body and reject it
          if (_init?.body) {
            // If body is FormData, it likely contains a file
            // If body is a Blob/File, it's definitely a file
            const body = _init.body
            if (body instanceof FormData || body instanceof Blob || body instanceof File) {
              const error = new Error('Web Speech API does not support file-based transcription. It only supports live streaming from a MediaStream. Please use the streaming transcription API or select a different provider that supports file-based transcription.')

View on GitHub (pinned to 677329427f)