CherryHQ/cherry-studio · error · ModelResolutionError

MODEL_RESOLUTION_FAILED

MODEL_RESOLUTION_FAILED

Error message

Failed to resolve model: ${modelId}

What it means

PluginEngine.resolveModel runs the plugin pipeline's 'resolveModel' hook (executeFirst) expecting a plugin to turn the string modelId into a LanguageModel. If no plugin handles it, executeFirst returns undefined and ModelResolutionError (code MODEL_RESOLUTION_FAILED) is thrown with modelId and providerId in context. The internal _internal_resolveModel plugin normally does this via the executor's registry.

Source

Thrown at packages/aiCore/src/core/runtime/pluginEngine.ts:124

  /**
   * Resolve modelId through the plugin pipeline (configureContext → resolveModel → wrapLanguageModel).
   * Returns a middleware-wrapped LanguageModel ready for external consumers like ToolLoopAgent.
   *
   * Note: This is a model-resolution-only path, not a full request lifecycle.
   * - `originalParams` in context will be `{}` since no request params exist at resolution time.
   * - `onError` hooks are NOT invoked on failure — callers should handle errors directly.
   */
  async resolveModel(modelId: string): Promise<LanguageModelV3> {
    const context = createContext(this.providerId, modelId, {})
    const manager = new PluginManager(this.basePlugins)

    // 1. configureContext — collect middlewares
    await manager.executeConfigureContext(context)

    // 2. resolveModel — string → LanguageModel
    const resolved = await manager.executeFirst<LanguageModel>('resolveModel', modelId, context)
    if (!resolved) {
      throw new ModelResolutionError(modelId, this.providerId)
    }
    if (!isV3Model(resolved)) {
      throw new ModelResolutionError(
        modelId,
        this.providerId,
        new Error(`Provider "${this.providerId}" resolved a non-V3 language model`)
      )
    }

    // 3. Apply middlewares
    if (context.middlewares && context.middlewares.length > 0) {
      return wrapLanguageModel({
        model: resolved,
        middleware: context.middlewares
      })
    }

    return resolved

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure the executor's resolveModel plugin is attached (usePlugins([executor.createResolveModelPlugin()])) before resolveModel.
  2. Prefer the high-level executor.streamText/generateText which wire the plugin automatically when model is a string.
  3. Confirm the model id exists for the provider in the registry.

Example fix

// before — engine has no resolver plugin
const model = await executor.pluginEngine.resolveModel('gpt-4o')
// after
executor.pluginEngine.usePlugins([executor.createResolveModelPlugin()])
const model = await executor.pluginEngine.resolveModel('gpt-4o')
Defensive patterns

Strategy: validation

Validate before calling

if (!executor.pluginEngine.getPlugins().some((p) => p.name === '_internal_resolveModel')) {
  executor.pluginEngine.usePlugins([executor.createResolveModelPlugin()])
}
await executor.pluginEngine.resolveModel(modelId)

Try / catch

try {
  await executor.pluginEngine.resolveModel(modelId)
} catch (e) {
  if (e instanceof ModelResolutionError) {
    // no resolver plugin attached or model id unknown — attach the plugin / verify the id
  }
}

Prevention

When it happens

Trigger: Calling resolveModel (directly or via resolveLanguageModel/streamText with a string model) when no resolveModel plugin is attached — e.g. calling executor.pluginEngine.resolveModel without first usePlugins([executor.createResolveModelPlugin()]).

Common situations: Using the pluginEngine standalone without registering the internal resolve plugin; calling resolveLanguageModel for a provider whose registry has no such model id; plugins were cleared/filtered removing the resolver.

Related errors


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