CherryHQ/cherry-studio · error · Error

Provider extension "${providerId}" not registered

Error message

Provider extension "${providerId}" not registered

What it means

createExecutor checks extensionRegistry.has(providerId) before doing anything else; if the id is not registered (by name or alias) it throws immediately, before attempting provider creation. This is the top-level guard for all convenience factories (streamText, generateText, generateImage, embedMany, rerank).

Source

Thrown at packages/aiCore/src/core/runtime/index.ts:36

} from './types'

// === 便捷工厂函数 ===

import { type AiPlugin } from '../plugins'
import { extensionRegistry } from '../providers'
import { type CoreProviderSettingsMap, type StringKeys } from '../providers/types'
import { RuntimeExecutor } from './executor'

/**
 * 创建运行时执行器 - 支持类型安全的已知provider
 * 自动确保 provider 已初始化
 */
export async function createExecutor<
  TSettingsMap extends Record<string, any> = CoreProviderSettingsMap,
  T extends StringKeys<TSettingsMap> = StringKeys<TSettingsMap>
>(providerId: T, options: TSettingsMap[T], plugins?: AiPlugin[]): Promise<RuntimeExecutor<TSettingsMap, T>> {
  if (!extensionRegistry.has(providerId)) {
    throw new Error(`Provider extension "${providerId}" not registered`)
  }

  const provider = await extensionRegistry.createProvider(providerId, options || {})

  // Extract model resolver from variant's resolveModel declaration (type-safe at extension level)
  const resolver = extensionRegistry.getModelResolver(providerId as string)
  const modelResolver = resolver ? (modelId: string) => resolver(provider, modelId) : undefined

  return RuntimeExecutor.create<TSettingsMap, T>(providerId, provider, options, plugins, modelResolver)
}

/**
 * Resolves a language model for any provider with its middleware applied.
 *
 * When `plugins` are provided, middleware contributed through
 * `configureContext` is applied to the returned model. This lets independently
 * resolved models, such as retry fallbacks, retain their model-specific
 * adapters.

View on GitHub (pinned to 726446b54c)

Solutions

  1. Check extensionRegistry.has(providerId) (or hasProviderConfig) before calling.
  2. Ensure the initialization module is imported so coreExtensions are registered.
  3. Register custom extensions before use; correct the id or use a known alias.

Example fix

// before
await streamText('opena1', { apiKey }, { model: 'gpt-4o', prompt: 'hi' })
// after
import '@cherrystudio/ai-core/provider' // ensures registration
await streamText('openai', { apiKey }, { model: 'gpt-4o', prompt: 'hi' })
Defensive patterns

Strategy: validation

Validate before calling

import { extensionRegistry } from '@cherrystudio/ai-core/provider'
if (!extensionRegistry.has(providerId)) {
  throw new Error(`Provider "${providerId}" not registered. Available: ${extensionRegistry.getAllProviderIds().join(', ')}`)
}
await createExecutor(providerId, options)

Prevention

When it happens

Trigger: Calling createExecutor (or streamText/generateText/generateImage/embedMany/rerank factory) with a providerId that was never registered and is not an alias — e.g. a typo or a custom provider registered later.

Common situations: Typo in provider id; custom extension not yet registered; calling before the initialization module (which runs extensionRegistry.registerAll(coreExtensions)) has loaded; tree-shaking stripping the init side-effect.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/b3b32c37ddf9960d. Report an issue: GitHub.