CherryHQ/cherry-studio · error · Error
Image generation job: no async transport for '${sdkConfig.pr
Error message
Image generation job: no async transport for '${sdkConfig.providerId}' (model '${sdkConfig.modelId}') What it means
Thrown in imageGenerationJobHandler.execute() when resolveImageTransport(providerId, modelId, settings) returns null. The imageTransportRegistry only registers poll-capable transports for ppio, dashscope, modelscope, and dmxapi-bespoke (gated by dmxapiUsesCustomTransport). A null means the provider/model is not in that registry — either a provider without an async transport was routed into the job path, or the registry's `supports` predicate rejected the model.
Source
Thrown at src/main/ai/provider/custom/tasks/imageGenerationJobHandler.ts:92
// `recovery: 'abandon'` no run outlives the process, so persisting it would be
// a write nobody reads — and would imply a durability this handler does not have.
const captureContext = createAiUsageCaptureContext({
providerId: provider.id,
providerName: provider.name,
modelId: sdkConfig.modelId,
modelName: model.name,
pricing: model.pricing,
trustProviderReportedCost: provider.apiFeatures.reportsActualCost,
reportedCostCurrency: provider.reportedCostCurrency,
credentialReceipt,
source: input.source ?? null,
messageRef: null
})
const usageStartedAt = Date.now()
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`)
}
View on GitHub (pinned to 726446b54c)
Solutions
- Gate the job path on hasImageTransport(providerId, modelId) before enqueue — single-shot providers must stay on the synchronous doGenerate path.
- If the provider genuinely needs async submit/poll, register a TransportRegistration in imageTransportRegistry.ts (supports + build).
- For DMXAPI, confirm dmxapiUsesCustomTransport(modelId) returns true for that model family.
- Surface a user-facing 'this model does not support async image generation' message instead of the generic throw.
Example fix
// before
const transport = resolveImageTransport(providerId, modelId, settings)
if (!transport) throw new Error(`... no async transport ...`)
// after — fail earlier at enqueue with an actionable message
if (!hasImageTransport(providerId, modelId)) {
throw new Error(`Model '${modelId}' (provider '${providerId}') has no async image transport; use the synchronous path`)
} Defensive patterns
Strategy: validation
Validate before calling
// Gate the job path on transport existence before enqueue
if (!hasImageTransport(providerId, modelId)) {
throw new Error(`No async image transport for '${providerId}/${modelId}'; use the synchronous path`)
} Type guard
export function isNoAsyncTransportError(e: unknown): boolean {
return e instanceof Error && /no async transport for/.test(e.message)
} Try / catch
try {
await job.execute(ctx)
} catch (e) {
if (isNoAsyncTransportError(e)) {
// route to the synchronous doGenerate path instead
}
throw e
} Prevention
- Gate the job path on hasImageTransport(providerId, modelId) at enqueue.
- Register a TransportRegistration for any provider that needs async submit/poll.
- Keep single-shot providers (ollama/ovms/silicon) on the synchronous in-SDK path.
When it happens
Trigger: An image-generation job is enqueued for a provider that has no async transport: ollama, ovms, silicon, moonshot, newapi, or a dmxapi model not matched by dmxapiUsesCustomTransport. Also if hasImageTransport returned true at enqueue time but the predicate disagrees at execute (registry drift), or providerId is unknown to the registry.
Common situations: Job path selected for a single-shot/sync provider (ollama/ovms/silicon) that should use the in-SDK doGenerate path instead; new provider added without registering a transport; DMXAPI model outside the bespoke set routed to the job path.
Related errors
- imageModel
- Use embeddingModel() for embedding endpoint type
- Use rerankingModel() for jina-rerank endpoint type
- errorData.error || 'Image generation failed'
- Unknown model: ${input.modelId}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/454d2fa2886871d5.
Report an issue: GitHub.