CherryHQ/cherry-studio · error · Error

${provider} returned a task id but does not implement pollin

Error message

${provider} returned a task id but does not implement polling

What it means

Thrown by the shared `createImageGenerationModel` adapter when `transport.submit()` returns a `taskId` but `transport.poll` is undefined. The submit→poll contract requires that any transport returning a task id also implement polling; otherwise the generated images can never be retrieved. This is a transport implementation contract violation.

Source

Thrown at src/main/ai/provider/custom/imageGenerationModel.ts:118

      const submitResult = await transport.submit({
        modelId,
        prompt: options.prompt,
        n: options.n,
        size: options.size,
        seed: options.seed,
        files: options.files,
        mask: options.mask,
        providerParams,
        signal: abortSignal
      })

      let urls: string[]
      if (submitResult.imageUrls) {
        urls = submitResult.imageUrls
      } else if (submitResult.taskId) {
        if (!transport.poll) {
          throw new Error(`${provider} returned a task id but does not implement polling`)
        }

        let cancelRequested = false
        const cancelRemoteTask = () => {
          if (cancelRequested) return
          cancelRequested = true
          void transport.cancel?.(submitResult.taskId as string).catch(() => {})
        }

        if (abortSignal?.aborted) {
          cancelRemoteTask()
          throw createAbortError('Image generation aborted')
        }

        abortSignal?.addEventListener('abort', cancelRemoteTask, { once: true })
        try {
          urls = await transport.poll(submitResult.taskId, { signal: abortSignal, onProgress })
        } finally {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Implement `poll(taskId, options)` on the transport so task results can be retrieved.
  2. If the transport is genuinely single-shot, have `submit` return `{ imageUrls }` directly instead of a `taskId`.
  3. Add a compile-time check that any transport with async submit also declares poll.

Example fix

// before
const transport = {
  async submit(input) {
    const { taskId } = await api.create(input)
    return { taskId } // poll missing
  }
}
// after
const transport = {
  async submit(input) {
    const { taskId } = await api.create(input)
    return { taskId }
  },
  async poll(taskId) {
    const result = await api.get(taskId)
    return result.images
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!('poll' in transport) || typeof transport.poll !== 'function') {
  throw new Error('Transport must implement poll() if submit() can return a taskId')
}

Type guard

const transportCanPoll = (
  t: ImageGenerationTransport
): t is ImageGenerationTransport & { poll: NonNullable<ImageGenerationTransport['poll']> } =>
  typeof t.poll === 'function'

Prevention

When it happens

Trigger: A custom `ImageGenerationTransport` whose `submit` returns `{ taskId }` but omits the optional `poll` method. All built-in async transports (DashScope, ModelScope, DMXAPI) implement both.

Common situations: Writing a new transport and forgetting `poll`; refactoring a single-shot transport to return a task id without adding polling; a transport stub used in tests.

Related errors


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