CherryHQ/cherry-studio · error · Error

Image generation job: provider '${providerId}' not found

Error message

Image generation job: provider '${providerId}' not found

What it means

Thrown at the start of imageGenerationJobHandler.execute() when providerService.getByProviderId(providerId) returns null — the provider record is not in the database. The providerId was parsed from the job payload's uniqueModelId, so this means the provider was removed (or never existed) between enqueue and execution.

Source

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

 * `painting.id` — the row exists before enqueue) and have this handler write the
 * result there, which registers `painting_file_ref` rows and makes GC correct for
 * free. Then switch `recovery` back to `'retry'` and restore the resume branch from
 * the recipe in `docs/references/job-and-scheduler/handler-authoring.md`.
 */
export const imageGenerationJobHandler: JobHandler<ImageGenerationJobPayload> = {
  recovery: 'abandon',
  defaultQueue: (input) => `image-generation.${parseUniqueModelId(input.uniqueModelId).providerId}`,
  defaultConcurrency: 2,
  // The transport already retries transient poll errors internally; a job-level
  // retry would re-submit and burn the user's vendor quota, so cap at 1 attempt
  // (parity with agent.task).
  defaultRetryPolicy: { maxAttempts: 1, backoff: 'none', baseDelayMs: 0, maxDelayMs: 0 },
  defaultTimeoutMs: 30 * 60_000,
  async execute(ctx) {
    const input = ctx.input
    const { providerId, modelId } = parseUniqueModelId(input.uniqueModelId)
    const provider = providerService.getByProviderId(providerId)
    if (!provider) throw new Error(`Image generation job: provider '${providerId}' not found`)
    const model = modelService.getByKey(providerId, modelId)
    if (!model) throw new Error(`Image generation job: model '${modelId}' not found for provider '${providerId}'`)

    const { config, credentialReceipt } = await resolveProviderAiSdkConfig(provider, model)
    const sdkConfig = {
      ...config,
      modelId: resolveWireModelId(model, resolveEffectiveEndpoint(provider, model).endpointType)
    }
    // Built fresh every execution and held in memory only. Upstream persists this
    // to job metadata so a resumed run can still attribute its cost; with
    // `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,

View on GitHub (pinned to 726446b54c)

Solutions

  1. Confirm the provider still exists: check providerService.getByProviderId(providerId) before enqueue and at execution.
  2. If the provider was intentionally removed, fail the job with a user-facing 'provider removed' message and clean up.
  3. Guard the uniqueModelId format (parseUniqueModelId) so malformed ids fail at enqueue, not at execute.
  4. Investigate DB consistency if the provider should exist.

Example fix

// before
const provider = providerService.getByProviderId(providerId)
if (!provider) throw new Error(`Image generation job: provider '${providerId}' not found`)
// after — include parse context
const provider = providerService.getByProviderId(providerId)
if (!provider) throw new Error(`Image generation job: provider '${providerId}' (from '${input.uniqueModelId}') not in DB; was it removed?`)
Defensive patterns

Strategy: validation

Validate before calling

// Validate provider existence before enqueue
const provider = providerService.getByProviderId(providerId)
if (!provider) throw new Error(`Cannot enqueue: provider '${providerId}' not found`)

Type guard

export function isJobProviderNotFound(e: unknown): boolean {
  return e instanceof Error && /Image generation job: provider .* not found/.test(e.message)
}

Try / catch

try {
  await job.execute(ctx)
} catch (e) {
  if (isJobProviderNotFound(e)) {
    // mark job failed with user-facing 'provider removed' reason
  }
  throw e
}

Prevention

When it happens

Trigger: A job is enqueued for a custom image-generation provider, then before/at execution the provider record is deleted from the DB, or the uniqueModelId's providerId segment is malformed/unknown. Because recovery is 'abandon', this surfaces during a fresh execution attempt rather than a resumed one.

Common situations: User deletes the provider while a job is queued, provider DB sync issue, uniqueModelId corruption, or a provider id typo in test fixtures.

Related errors


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