moeru-ai/airi · error

Failed to initialize speech provider

Error message

Failed to initialize speech provider

What it means

Thrown by the OpenAI-compatible audio speech playground when the provider factory resolves falsy. This provider targets arbitrary OpenAI-shaped endpoints, so its config needs an API key AND a base URL; notably the page's onMounted uses `??=` defaults that intentionally preserve empty strings, meaning a blank-but-present baseUrl or key is not auto-corrected and can reach the factory, which then returns `undefined` — exactly the case this guard converts into an explicit error.

Source

Thrown at packages/stage-pages/src/pages/settings/providers/speech/openai-compatible-audio-speech.vue:111

  { deep: true, immediate: true },
)

// Check if API key is configured
const apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey)

// Ensure provider config is initialized on mount
onMounted(() => {
  providers.value[providerId] ??= {}
  // Defaults only when unset (null/undefined); empty strings are kept intentionally
  providers.value[providerId].model ??= defaultModel
  providers.value[providerId].voice ??= defaultVoice
})

// Generate speech with OpenAI-compatible parameters
async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean, modelId?: string) {
  const provider = await providersStore.getProviderInstance<SpeechProvider<string>>(providerId)
  if (!provider) {
    throw new Error('Failed to initialize speech provider')
  }

  // Get provider configuration
  const providerConfig = providerStore.getProviderConfig(providerId)

  // Use the reactive model computed property (not a local variable)
  const modelToUse = modelId || model.value || defaultModel

  return await speechStore.speech(
    provider,
    modelToUse,
    input,
    voiceId || voice.value || defaultVoice,
    {
      ...providerConfig,
      ...defaultVoiceSettings,
      speed: speed.value,
    },

View on GitHub (pinned to 677329427f)

Solutions

  1. Fill in both a non-empty base URL (absolute, with scheme) and API key (use a placeholder like 'sk-none' for keyless local servers) and save.
  2. Verify the endpoint responds to an OpenAI-style models request before generating.
  3. Reload the page after saving so `getProviderInstance` rebuilds the instance.
  4. Check console for `Error creating provider instance for ...` for the construction failure detail.
  5. Report it if a fully valid endpoint still yields a falsy instance.

Example fix

// before
onMounted(() => {
  providers.value[providerId] ??= {}
  providers.value[providerId].model ??= defaultModel
  providers.value[providerId].voice ??= defaultVoice
})

// after (also default the connection fields, and validate before generate)
onMounted(() => {
  providers.value[providerId] ??= {}
  providers.value[providerId].model ??= defaultModel
  providers.value[providerId].voice ??= defaultVoice
})
const canGenerate = computed(() => {
  const c = providers.value[providerId]
  return !!c?.baseUrl?.trim() && !!c?.apiKey?.trim()
})
// template: <button :disabled="!canGenerate">Generate</button>
Defensive patterns

Strategy: validation

Validate before calling

const canGenerate = computed(() => {
  const c = providers.value[providerId]
  return !!c?.baseUrl?.trim() && !!c?.apiKey?.trim()
})
if (!canGenerate.value)
  throw new Error('Set both a base URL and an API key (placeholder allowed for keyless servers)')

Type guard

function isSpeechProvider(v: unknown): v is SpeechProvider<string> {
  return typeof v === 'object' && v !== null && typeof (v as SpeechProvider<string>).speech === 'function'
}

Try / catch

try { const provider = await providersStore.getProviderInstance<SpeechProvider<string>>(providerId) }
catch (error) { showError(errorMessageFrom(error)) }

Prevention

When it happens

Trigger: Clicking Generate with baseUrl or apiKey saved as empty strings (the `??=` defaults skip them); a self-hosted endpoint whose URL lacks scheme/path so the factory declines; pointing at an endpoint that is not OpenAI-compatible at construction time.

Common situations: Using local servers (LocalAI, vLLM, llama.cpp server) that were not started or moved ports; pasting a URL with trailing path issues; storing the key in one compatible-provider entry while generating from another; assuming defaults were applied when the fields were left blank on purpose (they are kept).

Related errors


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