moeru-ai/airi · error · Error

Failed to resolve chat provider

Error message

Failed to resolve chat provider "${providerId}"

What it means

After passing the provider/model precondition, executeSend asks providerStore.getProviderInstance<ChatProvider>(providerId) for a live provider instance and throws when it cannot produce one. This means the id is 'active' in settings but no configured, instantiable provider exists behind it — removed, incompletely configured, or the providers store not yet initialized.

Solutions

  1. Open provider settings and reconfigure/re-add the chat provider referenced by the error message.
  2. Re-select the active provider and model in chat settings after changing providers.
  3. Await providers store initialization before enabling the send action.
  4. When a provider is removed, clear activeProvider/activeModel so users hit the clearer error 147 instead.

Example fix

// before
await chatStore.send({ sessionId, text })

// after
const provider = providersStore.providers[activeProvider.value]
if (!provider || provider.status !== 'configured') {
  toast.info('Configure the active chat provider before sending')
  return
}
await chatStore.send({ sessionId, text })
Defensive patterns

Strategy: validation

Validate before calling

const provider = providersStore.providers[activeProvider.value]
if (!provider || (provider.status !== 'configured' && provider.status !== 'bypassed')) {
  openProviderSettings()
  return
}
await chatStore.send({ sessionId, text })

Type guard

function isResolvableChatProvider(providerId: string | undefined, providers: Record<string, InferenceServiceProvider>): boolean {
  if (!providerId) return false
  const p = providers[providerId]
  return Boolean(p && (p.status === 'configured' || p.status === 'bypassed'))
}

Try / catch

try {
  await chatStore.send({ sessionId, text })
}
catch (error) {
  const message = errorMessageFrom(error) ?? ''
  if (message.startsWith('Failed to resolve chat provider')) {
    openProviderSettings(message.slice(message.indexOf('"'))) // includes the id
    return
  }
  throw error
}

Prevention

When it happens

Trigger: activeProvider references a provider deleted from settings without resetting chat settings; provider record exists but its config lacks credentials so instantiation returns nothing; send fires before the providers store finished initialization; provider definition no longer registered (paired with error 142's registry).

Common situations: Deleting or resetting a provider in settings then immediately sending from a still-open chat; storage partial restore that kept chat settings but lost provider records; version changes renaming provider definitions.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-08-28). Data as JSON: /api/errors/dbaa4eee1225af16. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-ui/src/stores/chat.ts:424

    chatSession.appendSessionMessage(sessionId, {
      role: 'error',
      content: errorMessageFrom(error) ?? 'Unknown chat operation failure',
    })
  }

  async function executeSend(payload: ChatSendPayload): Promise<ChatSendResult> {
    const providerId = activeProvider.value
    const modelId = activeModel.value
    if (!providerId || !modelId)
      throw new Error('No active chat provider or model configured')

    if (!await chatSession.loadSession(payload.sessionId))
      throw new Error('Failed to load the target chat session')

    const messageCount = chatSession.getSessionMessages(payload.sessionId).length
    const chatProvider = await consciousnessStore.getChatProviderInstance(providerId)
    if (!chatProvider)
      throw new Error(`Failed to resolve chat provider "${providerId}"`)

    await runtime.ingest(payload.text, {
      model: modelId,
      chatProvider,
      attachments: payload.attachments,
      input: payload.input,
      replyToMessageId: payload.replyToMessageId,
      toolReferences: payload.tools,
      temperature: payload.temperature ?? consciousnessStore.activeTemperature,
      topP: payload.topP ?? consciousnessStore.activeTopP,
      // Resolve this function after the request reaches the per-session queue.
      // The history then contains tool names from every earlier queued turn.
      tools: async () => {
        const references = collectToolReferences(payload.sessionId, payload.tools)
        return llmToolsStore.getToolsByNames(...references.map(tool => tool.name))
      },
    }, payload.sessionId)

View on GitHub (pinned to 438a067dde)