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
- Feature-detect before constructing — only call createWebSpeechAPIProvider when the constructor exists
- Construct the provider lazily at selection time instead of at module or list load
- In Firefox, steer users to a server-side transcription provider
- 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
- Lazy-construct browser-gated providers at selection, not import time
- Hide unavailable providers from the picker via feature detection
- Run unit tests for such providers only in DOM-environment test files
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
- 1. Microphone permission not granted - browser should…
- 1. Microphone permission not granted - browser should…
- 2. Recognition already running - this is normal if called…
- 2. Recognition already running - this is normal if called…
- 3. Browser requires user gesture - ensure microphone was…
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)