{"record":{"id":"1872f445f6352c51","repo":"moeru-ai/airi","slug":"failed-to-initialize-speech-provider-1872f4","errorCode":null,"errorMessage":"Failed to initialize speech provider","messagePattern":"Failed to initialize speech provider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-pages/src/pages/settings/providers/speech/elevenlabs.vue","lineNumber":55,"sourceCode":"const speechStore = useSpeechStore()\nconst providersStore = useProviderStore()\nconst providerStore = useProviderConfigStore()\nconst { configs: providers } = storeToRefs(providerStore)\nconst { t } = useI18n()\n\n// Check if API key is configured\nconst apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey)\n\n// Get available voices for ElevenLabs\nconst availableVoices = computed(() => {\n  return speechStore.availableVoices[providerId] || []\n})\n\n// Generate speech with ElevenLabs-specific parameters\nasync function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean) {\n  const provider = await providersStore.getProviderInstance(providerId) as SpeechProviderWithExtraOptions<string, UnElevenLabsOptions>\n  if (!provider) {\n    throw new Error('Failed to initialize speech provider')\n  }\n\n  // Get provider configuration\n  const providerConfig = providerStore.getProviderConfig(providerId)\n\n  // Get model from configuration or use default\n  const model = providerConfig.model as string | undefined || defaultModel\n\n  // ElevenLabs doesn't need SSML conversion, but if SSML is provided, use it directly\n  return await speechStore.speech(\n    provider,\n    model,\n    input,\n    voiceId,\n    {\n      ...providerConfig,\n      ...defaultVoiceSettings,\n    },","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-pages/src/pages/settings/providers/speech/elevenlabs.vue#L37-L73","documentation":"Thrown by the ElevenLabs speech playground when `getProviderInstance('elevenlabs')` resolves falsy. The ElevenLabs definition (packages/stage-ui/src/libs/providers/providers/elevenlabs/index.ts:90) builds the instance via `createUnElevenLabs(config.apiKey.trim(), config.baseUrl ?? 'https://unspeech.hyp3r.link/v1/')`; although the static type says non-nullable, the underlying unspeech factory can yield `undefined` when the key is blank, and this page guard converts that into an explicit error before `speechStore.speech()` is called with a dead provider.","triggerScenarios":"Clicking Generate with `providers.value.elevenlabs.apiKey` empty or whitespace; config saved with a key but the unspeech factory silently declining it; baseUrl left unset is fine (it falls back to the unspeech bridge) — the failing input is the missing key.","commonSituations":"Trying the playground before finishing provider setup; clearing the key field and saving; copy/pasting a key with quotes or spaces so `.trim()` yields an unusable credential; stale page state after revoking a key in the ElevenLabs dashboard.","solutions":["Save a valid ElevenLabs API key in Settings → Providers → ElevenLabs and pass the built-in validators (they require apiKey and an absolute baseUrl with trailing slash when overridden).","Reload the settings page after saving so `getProviderInstance` builds from current config.","Inspect console output for `Error creating provider instance for elevenlabs:` preceding this throw.","If a custom baseUrl is set, confirm it is absolute with a trailing slash — the validator flags that separately but the factory can also fail on it.","Treat persistent falsy results with a valid key as a factory bug (contract violation) and report it upstream."],"exampleFix":"// before\nconst provider = await providersStore.getProviderInstance(providerId) as SpeechProviderWithExtraOptions<string, UnElevenLabsOptions>\nif (!provider)\n  throw new Error('Failed to initialize speech provider')\n\n// after\nconst config = providerStore.getProviderConfig(providerId)\nif (!config?.apiKey?.trim())\n  throw new Error('ElevenLabs API key is empty. Save it in provider settings first.')\nconst provider = await providersStore.getProviderInstance(providerId) as SpeechProviderWithExtraOptions<string, UnElevenLabsOptions>\nif (!provider || typeof provider.speech !== 'function')\n  throw new Error('ElevenLabs factory returned no usable instance — check key/baseUrl and console logs')","handlingStrategy":"validation","validationCode":"const canGenerate = computed(() => {\n  const c = providers.value[providerId]\n  return !!c?.apiKey?.trim() && (!c?.baseUrl || /^https?:\\/\\/.+\\/$/.test(c.baseUrl.trim()))\n})","typeGuard":"function isSpeechProvider(v: unknown): v is SpeechProviderWithExtraOptions<string, UnElevenLabsOptions> {\n  return typeof v === 'object' && v !== null && typeof (v as SpeechProviderWithExtraOptions<string>).speech === 'function'\n}","tryCatchPattern":"try { const provider = await providersStore.getProviderInstance(providerId) }\ncatch (error) { // store-level: credentials/definition missing — surface message directly\n  showError(errorMessageFrom(error)) }","preventionTips":["Require a trimmed non-empty API key before enabling the playground.","When overriding baseUrl, validate absolute URL with trailing slash at save time.","Reload the page after credential changes so the cached instance rebuilds."],"tags":["speech","provider","initialization","api-key","elevenlabs"],"backgroundTag":"provider-instance-undefined","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}