CherryHQ/cherry-studio · error · Error

Image generation job: model '${modelId}' not found for provi

Error message

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

What it means

Thrown immediately after the provider lookup in imageGenerationJobHandler.execute() when modelService.getByKey(providerId, modelId) returns null — the model record is missing for that provider. Like 397, it reflects a stale payload: the model existed at enqueue time but not at execution.

Source

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

 * 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,
      trustProviderReportedCost: provider.apiFeatures.reportsActualCost,
      reportedCostCurrency: provider.reportedCostCurrency,

View on GitHub (pinned to 726446b54c)

Solutions

  1. Confirm the model still exists: check modelService.getByKey(providerId, modelId) before enqueue and at execution.
  2. If the model was removed, fail the job with a user-facing 'model removed' message.
  3. Validate the provider↔model pairing when building uniqueModelId.
  4. Investigate model catalog sync if the model should exist.

Example fix

// before
const model = modelService.getByKey(providerId, modelId)
if (!model) throw new Error(`Image generation job: model '${modelId}' not found for provider '${providerId}'`)
// after — hint at the likely cause
if (!model) throw new Error(`Image generation job: model '${modelId}' for provider '${providerId}' not in DB; was it removed or renamed?`)
Defensive patterns

Strategy: validation

Validate before calling

const model = modelService.getByKey(providerId, modelId)
if (!model) throw new Error(`Cannot enqueue: model '${modelId}' not found for provider '${providerId}'`)

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: A job enqueued for a specific provider+model combination, but the model record was deleted (or provider/model mismatch) before execution. The modelId is parsed from uniqueModelId; a wrong pairing or a removed model triggers it.

Common situations: User removes the model while a job is queued, model sync lag, modelId drift after a rename, or uniqueModelId built with the wrong provider/model pairing.

Related errors


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