CherryHQ/cherry-studio · error · Error

Missing modelDescriptor for DashScope model: ${input.modelId

Error message

Missing modelDescriptor for DashScope model: ${input.modelId}

What it means

Thrown by `DashScopeTransport.submit` when `input.modelDescriptor` is undefined. The descriptor carries per-model routing (endpoint path, sync/async flag, response family) derived from the registry's `vendorTransport`; without it the transport cannot pick an endpoint or parse the response. This is a wiring invariant, not a user input.

Source

Thrown at src/main/ai/provider/custom/dashscope/dashscopeTransport.ts:381

    default:
      throw new Error(`Unsupported DashScope image model: ${descriptor.id}`)
  }
}

class DashScopeTransport implements ImageGenerationTransport {
  private apiKey: string
  private baseURL: string
  private pendingDescriptors = new Map<string, DashScopeModelDescriptor>()

  constructor(settings: DashScopeTransportSettings) {
    this.apiKey = settings.apiKey
    this.baseURL = settings.imageBaseURL || DEFAULT_DASHSCOPE_IMAGE_BASE_URL
  }

  async submit(input: ImageGenerationSubmitInput): Promise<{ taskId?: string; imageUrls?: string[] }> {
    const descriptor = input.modelDescriptor
    if (!descriptor) {
      throw new Error(`Missing modelDescriptor for DashScope model: ${input.modelId}`)
    }

    const body = buildRequestBody(input, descriptor)
    const extraHeaders: Record<string, string> = descriptor.isSync ? {} : { 'X-DashScope-Async': 'enable' }

    const response = await this.request<DashScopeTaskResult>(descriptor.endpoint, 'POST', body, {
      timeout: 120000,
      signal: input.signal,
      extraHeaders
    })

    if (descriptor.isSync) {
      return { imageUrls: extractImageUrls(response.output, responseFamilyFor(descriptor)) }
    }

    const taskId = response.output.task_id
    if (!taskId) {
      throw new DashScopeApiError('DashScope async submit returned no task_id', 0)

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure `paintingPipeline.ts` resolves and injects `modelDescriptor` from the registry's `modes[mode].vendorTransport` into the submit input.
  2. Add a `vendorTransport` entry (endpoint, isSync) for the affected model/mode in the registry.
  3. Guard upstream: reject model selections whose registry mode has no `vendorTransport` before reaching the transport.

Example fix

// before — submit called with no descriptor
await transport.submit({ modelId, prompt, ... })
// after — pipeline injects the descriptor
await transport.submit({ modelId, prompt, modelDescriptor: { id: modelId, endpoint, isSync }, ... })
Defensive patterns

Strategy: validation

Validate before calling

if (!input.modelDescriptor) {
  throw new Error(`No vendorTransport registered for model ${input.modelId} — check the registry's modes[mode].vendorTransport`)
}

Type guard

const hasModelDescriptor = (
  input: ImageGenerationSubmitInput
): input is ImageGenerationSubmitInput & { modelDescriptor: ImageTransportDescriptor } =>
  input.modelDescriptor != null

Prevention

When it happens

Trigger: The painting pipeline called `submit` without injecting `modelDescriptor` into the `ImageGenerationSubmitInput`; the registry mode for the selected model lacks a `vendorTransport` entry; a code path bypassed `paintingPipeline.ts` which normally populates the descriptor.

Common situations: A new image mode was added to the registry without a `vendorTransport` block; a refactor stopped threading the descriptor through the job payload; a model was selected whose registry entry predates the descriptor mechanism.

Related errors


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