{"record":{"id":"261f66ddfb0a2a46","repo":"moeru-ai/airi","slug":"provider-credentials-for-providerid-not-found","errorCode":null,"errorMessage":"Provider credentials for ${providerId} not found","messagePattern":"Provider credentials for (.+?) not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/stores/providers/provider.ts","lineNumber":820,"sourceCode":"  >(providerId: string): Promise<R> {\n    await waitForProviderMetadata()\n    const cached = providerInstanceCache.get(providerId) as R | undefined\n    if (cached)\n      return cached\n\n    const definition = getProviderDefinition(providerId)\n\n    // Providers that don't require credentials use empty config\n    let config = providerCredentials.value[providerId]\n    const noCredentials = definition.requiresCredentials === false || providerId === 'browser-web-speech-api'\n    if (!config && noCredentials) {\n      config = getDefaultProviderConfig(providerId) || {}\n      const definitionId = getProviderDefinitionId(providerId)\n      providerConfigStore.ensureProvider(providerId, definitionId, config)\n    }\n\n    if (!config && !noCredentials)\n      throw new Error(`Provider credentials for ${providerId} not found`)\n\n    try {\n      const instance = await definition.createProvider(config || {})\n      providerInstanceCache.set(providerId, instance)\n      return instance as R\n    }\n    catch (error) {\n      console.error(`Error creating provider instance for ${providerId}:`, error)\n      throw error\n    }\n  }\n\n  /**\n   * Passes AIRI chat options to the provider that owns their wire representation.\n   * The cached base instance remains unchanged for consumers that do not opt in.\n   */\n  async function getChatProviderInstance(\n    providerId: string,","sourceCodeStart":802,"sourceCodeEnd":838,"githubUrl":"https://github.com/moeru-ai/airi/blob/f679616c34f1cf6d282c8d64264242af944b3fed/packages/stage-ui/src/stores/providers/provider.ts#L802-L838","documentation":"getProviderInstance is the single construction path for providers. It reads saved credentials from providerCredentials; when none exist and the definition does not declare requiresCredentials === false (and it is not the special-cased 'browser-web-speech-api'), it throws this before ever calling createProvider.","triggerScenarios":"Awaiting providersStore.getProviderInstance(id) for a provider whose API key/config was never saved; a providerId typo that matches no saved credentials entry; credentials wiped by logout or storage clear while the provider stayed selected.","commonSituations":"Fresh install where a provider is selected before credentials exist; tests instantiating the real store without seeding providerCredentials; switching devices; provider renamed so the old id has no credentials entry.","solutions":["Open Settings > Providers, save credentials for the provider, and retry.","Verify the providerId string matches the definition id exactly (case-sensitive).","If the provider genuinely needs no credentials, add requiresCredentials: false to its definition.","In tests, seed providerCredentials or use the credential-less 'browser-web-speech-api' provider."],"exampleFix":"// before\nconst provider = await providersStore.getProviderInstance('openai')\n\n// after\nif (!providerCredentials.value['openai'] && getProviderDefinition('openai').requiresCredentials !== false) {\n  throw new Error('Save the OpenAI API key in Settings > Providers before using it')\n}\nconst provider = await providersStore.getProviderInstance('openai')","handlingStrategy":"validation","validationCode":"const needsCredentials = definition.requiresCredentials !== false && providerId !== 'browser-web-speech-api'\nif (needsCredentials && !providerCredentials.value[providerId]) {\n  // route the user to provider settings instead of calling getProviderInstance\n}","typeGuard":null,"tryCatchPattern":"try {\n  const instance = await providersStore.getProviderInstance(providerId)\n}\ncatch (e) {\n  if (errorMessageFrom(e)?.includes('credentials'))\n    openProviderSettings(providerId)\n  else\n    throw e\n}","preventionTips":["Derive a computed isProviderConfigured(id) from providerCredentials and gate feature entrypoints on it.","Never persist a provider selection without requiring its credentials.","Treat 'credentials not found' as a user-actionable state, not a crash."],"tags":["provider","credentials","api-key","configuration"],"backgroundTag":"missing-api-key","analyzedSha":"f679616c34f1cf6d282c8d64264242af944b3fed","analyzedAt":"2026-08-28T13:11:01.995Z","contentChangedAt":"2026-08-28T13:11:01.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}