CherryHQ/cherry-studio · error · Error

Image generation submit for '${sdkConfig.modelId}' returned

Error message

Image generation submit for '${sdkConfig.modelId}' returned neither imageUrls nor a taskId

What it means

Error "Image generation submit for '${sdkConfig.modelId}' returned neither imageUrls nor a taskId" thrown in CherryHQ/cherry-studio.

Source

Thrown at src/main/ai/provider/custom/tasks/imageGenerationJobHandler.ts:108

    const transport = resolveImageTransport(sdkConfig.providerId, sdkConfig.modelId, sdkConfig.providerSettings)
    if (!transport) {
      throw new Error(
        `Image generation job: no async transport for '${sdkConfig.providerId}' (model '${sdkConfig.modelId}')`
      )
    }

    // No persisted-task resume branch: `recovery: 'abandon'` means a job never
    // outlives the process that enqueued it, so every execution starts at submit.
    let urls: string[]
    const submit = await transport.submit(await buildSubmitInput(input, sdkConfig.modelId, ctx.signal))
    if (submit.imageUrls) {
      urls = submit.imageUrls
    } else if (submit.taskId) {
      urls = await pollUntilDone(transport, submit.taskId, ctx)
    } else {
      // A malformed submit response (neither URLs nor a task id) must fail the
      // job rather than silently complete with zero files (a paid no-op).
      throw new Error(`Image generation submit for '${sdkConfig.modelId}' returned neither imageUrls nor a taskId`)
    }

    // Record before local download: the provider invocation completed even if file
    // persistence fails. Polling is part of this invocation, not another billable
    // call; a successful zero-image response is still an observable invocation.
    if (captureContext) {
      const completedAt = Date.now()
      aiUsageRecordService.recordInvocation({
        requestId: `custom-image:${ctx.jobId}`,
        context: captureContext,
        modality: 'image',
        imageCount: urls.length,
        metrics: { timeCompletionMs: Math.max(0, completedAt - usageStartedAt) },
        completedAt
      })
    }

    // An empty URL list from a *successful* submit/poll (e.g. content moderation

View on GitHub (pinned to 726446b54c)

Solutions

  1. Check the image transport implementation for the model: it must return either imageUrls for synchronous results or a taskId for async polling.
  2. Verify the provider's image transport response parsing — some providers nest URLs under data[].url or output fields.
  3. Enable debug logging for the provider transport to inspect the raw submit response and confirm which field carries the result.

Example fix

In the provider's submit response parser, return { imageUrls } or { taskId }; e.g. return { taskId: data.task_id } when the API is asynchronous.

When it happens

Trigger: An async image generation transport's submit() resolves with neither imageUrls nor taskId — a malformed vendor response that the job handler cannot act on.

Common situations: Occurs when a custom image provider (DashScope, ModelScope, PPIO, etc.) returns a degraded or unexpected submit response, e.g. content moderation short-circuiting the task or a vendor API schema change. The job fails instead of silently completing with zero files (a paid no-op).


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