{"record":{"id":"8e47f7a023d545ab","repo":"CherryHQ/cherry-studio","slug":"provider-returned-a-task-id-but-does-not-implem","errorCode":null,"errorMessage":"${provider} returned a task id but does not implement polling","messagePattern":"(.+?) returned a task id but does not implement polling","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/provider/custom/imageGenerationModel.ts","lineNumber":118,"sourceCode":"\n      const submitResult = await transport.submit({\n        modelId,\n        prompt: options.prompt,\n        n: options.n,\n        size: options.size,\n        seed: options.seed,\n        files: options.files,\n        mask: options.mask,\n        providerParams,\n        signal: abortSignal\n      })\n\n      let urls: string[]\n      if (submitResult.imageUrls) {\n        urls = submitResult.imageUrls\n      } else if (submitResult.taskId) {\n        if (!transport.poll) {\n          throw new Error(`${provider} returned a task id but does not implement polling`)\n        }\n\n        let cancelRequested = false\n        const cancelRemoteTask = () => {\n          if (cancelRequested) return\n          cancelRequested = true\n          void transport.cancel?.(submitResult.taskId as string).catch(() => {})\n        }\n\n        if (abortSignal?.aborted) {\n          cancelRemoteTask()\n          throw createAbortError('Image generation aborted')\n        }\n\n        abortSignal?.addEventListener('abort', cancelRemoteTask, { once: true })\n        try {\n          urls = await transport.poll(submitResult.taskId, { signal: abortSignal, onProgress })\n        } finally {","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/provider/custom/imageGenerationModel.ts#L100-L136","documentation":"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.","triggerScenarios":"A custom `ImageGenerationTransport` whose `submit` returns `{ taskId }` but omits the optional `poll` method. All built-in async transports (DashScope, ModelScope, DMXAPI) implement both.","commonSituations":"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.","solutions":["Implement `poll(taskId, options)` on the transport so task results can be retrieved.","If the transport is genuinely single-shot, have `submit` return `{ imageUrls }` directly instead of a `taskId`.","Add a compile-time check that any transport with async submit also declares poll."],"exampleFix":"// before\nconst transport = {\n  async submit(input) {\n    const { taskId } = await api.create(input)\n    return { taskId } // poll missing\n  }\n}\n// after\nconst transport = {\n  async submit(input) {\n    const { taskId } = await api.create(input)\n    return { taskId }\n  },\n  async poll(taskId) {\n    const result = await api.get(taskId)\n    return result.images\n  }\n}","handlingStrategy":"type-guard","validationCode":"if (!('poll' in transport) || typeof transport.poll !== 'function') {\n  throw new Error('Transport must implement poll() if submit() can return a taskId')\n}","typeGuard":"const transportCanPoll = (\n  t: ImageGenerationTransport\n): t is ImageGenerationTransport & { poll: NonNullable<ImageGenerationTransport['poll']> } =>\n  typeof t.poll === 'function'","tryCatchPattern":null,"preventionTips":["Any transport returning a taskId must implement poll — make it a compile-time rule","For single-shot transports, return { imageUrls } from submit instead","Unit-test the submit/poll pairing on every new transport"],"tags":["transport","internal","contract","polling","image-generation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}