{"record":{"id":"cb4d07e76c9925b8","repo":"nexu-io/open-design","slug":"openrouter-image-response-contained-no-images-for","errorCode":null,"errorMessage":"openrouter image response contained no images for model ${wireModel}: ${truncate(text, 200)}","messagePattern":"openrouter image response contained no images for model (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":1884,"sourceCode":"    signal: AbortSignal.timeout(Math.max(OPENAI_IMAGE_HEADERS_TIMEOUT_MS, OPENAI_IMAGE_BODY_TIMEOUT_MS)),\n  }));\n  const text = await resp.text();\n  if (!resp.ok) {\n    throw new Error(`openrouter image ${resp.status}: ${truncate(text, 240)}`);\n  }\n\n  let data: any;\n  try {\n    data = JSON.parse(text);\n  } catch {\n    throw new Error(`openrouter image non-JSON response: ${truncate(text, 200)}`);\n  }\n\n  // Extract the first generated image from the response.\n  const images: any[] | undefined =\n    data?.choices?.[0]?.message?.images;\n  if (!images || images.length === 0) {\n    throw new Error(\n      `openrouter image response contained no images for model ${wireModel}: `\n      + truncate(text, 200),\n    );\n  }\n\n  const dataUrl: string | undefined = images[0]?.image_url?.url;\n  if (!dataUrl) {\n    throw new Error(\n      `openrouter image response missing image_url.url: ${truncate(text, 200)}`,\n    );\n  }\n\n  // Strip the data URL prefix (e.g. \"data:image/png;base64,\") and\n  // decode the remaining base64 payload.\n  const b64Match = dataUrl.match(/^data:image\\/[^;]+;base64,(.+)$/s);\n  let bytes: Buffer;\n  if (b64Match) {\n    bytes = Buffer.from(b64Match[1]!, 'base64');","sourceCodeStart":1866,"sourceCodeEnd":1902,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L1866-L1902","documentation":"Thrown by renderOpenRouterImage when the parsed response has no `choices[0].message.images` array (or it is empty). OpenRouter embeds generated images under that non-standard path for multi-modal chat models; an empty/missing array means the model ran but produced no image — usually because the model is text-only, the prompt was refused, or the model returned only text.","triggerScenarios":"wireModel is a text-only chat model mistaken for an image model, the model returned a textual refusal in message.content instead of an image, the underlying provider returned an empty image set, or OpenRouter changed the image embedding path.","commonSituations":"Selecting a chat model that lacks image output capability, a prompt the underlying provider declined, or an OpenRouter schema revision moving images to a different field.","solutions":["Confirm wireModel is listed at openrouter.ai/models as supporting image output (the `[\"image\",\"text\"]` modality).","Inspect choices[0].message.content — a textual refusal explains the empty image array.","Rephrase the prompt and retry.","If OpenRouter moved the field, update the extraction at apps/daemon/src/media/index.ts:1879."],"exampleFix":"// before\nif (!images || images.length === 0) {\n  throw new Error(`openrouter image response contained no images for model ${wireModel}: ${truncate(text, 200)}`);\n}\n\n// after — include any textual refusal reason\nconst refusal = data?.choices?.[0]?.message?.content;\nthrow new Error(\n  `openrouter image response had no images for ${wireModel}`\n  + (refusal ? ` (message: ${truncate(String(refusal), 160)})` : '')\n  + `: ${truncate(text, 200)}`,\n);","handlingStrategy":"type-guard","validationCode":"// Detect textual refusal vs empty image array\nfunction extractOpenRouterImage(data: any, wireModel: string): any[] {\n  const images = data?.choices?.[0]?.message?.images;\n  if (Array.isArray(images) && images.length > 0) return images;\n  const content = data?.choices?.[0]?.message?.content;\n  const reason = typeof content === 'string' ? content : JSON.stringify(content ?? '');\n  throw new Error(`openrouter image: no images for ${wireModel} (message: ${truncate(reason, 160)})`);\n}","typeGuard":"function isOpenRouterImageArray(v: unknown): v is { image_url: { url: string } }[] {\n  return Array.isArray(v) && v.length > 0 && v.every((i: any) => i?.image_url?.url);\n}","tryCatchPattern":"try {\n  const images = extractOpenRouterImage(data, wireModel);\n  // ...\n} catch (e) {\n  if (/no images for/.test(String(e))) { /* surface 'model produced no image' to user */ }\n  throw e;\n}","preventionTips":["Validate the chosen slug is image-capable in the OpenRouter catalogue before dispatch.","When the images array is empty, inspect choices[0].message.content for a refusal explanation.","Reword prompts that trigger refusals instead of retrying unchanged.","Track which models return empty arrays most often and warn users at selection time."],"tags":["openrouter","image-generation","response-shape","content-moderation","provider-api"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}