CherryHQ/cherry-studio · error · DashScopeApiError

0

0

Error message

DashScope async submit returned no task_id

What it means

Thrown as a `DashScopeApiError` (statusCode `0`, meaning not a real HTTP failure) when an async submit returns HTTP 2xx but `response.output.task_id` is missing. A well-formed async DashScope submit must return a task id to poll; its absence means the response shape does not match the async contract — likely a routing mismatch (a sync model treated as async) or an undocumented API change.

Source

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

      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)
    }
    this.pendingDescriptors.set(taskId, descriptor)
    return { taskId }
  }

  async poll(
    taskId: string,
    options: {
      signal?: AbortSignal
      onProgress?: (progress: number) => void
      modelDescriptor?: ImageTransportDescriptor
    }
  ): Promise<string[]> {
    // On a cross-restart resume the in-memory descriptor is gone (the transport is
    // rebuilt without the submit-time `pendingDescriptors` entry); fall back to the
    // descriptor persisted in the job input so the response family is still
    // resolved correctly instead of defaulting to `results`.
    const descriptor = this.pendingDescriptors.get(taskId) ?? options.modelDescriptor

View on GitHub (pinned to 726446b54c)

Solutions

  1. Verify the model's `isSync` flag in the registry matches its actual response behavior — sync models return images directly, not a task id.
  2. Log the raw submit response body to confirm the envelope shape, then align the descriptor's endpoint/isSync.
  3. If DashScope changed the envelope, update the `DashScopeTaskOutput.task_id` extraction path.

Example fix

// before — descriptor marked async for a sync model
{ id: 'z-image-turbo', endpoint: '/api/v1/services/aigc/multimodal-generation/generation' /* isSync missing */ }
// after
{ id: 'z-image-turbo', endpoint: '/api/v1/services/aigc/multimodal-generation/generation', isSync: true }
Defensive patterns

Strategy: try-catch

Validate before calling

const response = await transport.submit(input)
if (!descriptor.isSync && !response.taskId) {
  throw new Error(`Expected task_id from ${descriptor.endpoint} but got none — verify isSync routing`)
}

Try / catch

try {
  const { taskId } = await transport.submit(input)
} catch (e) {
  if (e instanceof DashScopeApiError && e.statusCode === 0 && /no task_id/.test(e.message)) {
    // routing mismatch — re-check descriptor.isSync / endpoint
  }
  throw e
}

Prevention

When it happens

Trigger: The descriptor's `isSync` flag is false but the endpoint actually returns synchronously (no task_id); DashScope changed its async response envelope; the wrong endpoint path was used for the model family.

Common situations: A model family was misclassified as async in the registry's `vendorTransport`; DashScope deprecated an endpoint and returns a different body; a sync model's descriptor `isSync` was left unset.

Related errors


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