{"record":{"id":"caf7c6b80352c774","repo":"nexu-io/open-design","slug":"openrouter-image-resp-status-truncate-text","errorCode":null,"errorMessage":"openrouter image ${resp.status}: ${truncate(text, 240)}","messagePattern":"openrouter image (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":1870,"sourceCode":"    aspect_ratio: aspectRatio,\n    image_size: '1K',\n  };\n  body.image_config = imageConfig;\n\n  const resp = await fetch(`${baseUrl}/chat/completions`, withMediaRequestInit(ctx, {\n    method: 'POST',\n    headers: {\n      'authorization': `Bearer ${credentials.apiKey}`,\n      'content-type': 'application/json',\n      'HTTP-Referer': 'https://opendesign.dev',\n      'X-Title': 'Open Design',\n    },\n    body: JSON.stringify(body),\n    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  }","sourceCodeStart":1852,"sourceCodeEnd":1888,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L1852-L1888","documentation":"Thrown by renderOpenRouterImage when POST /api/v1/chat/completions returns non-2xx. The request carries authorization bearer, HTTP-Referer, X-Title attribution headers, and an AbortSignal.timeout capped by max(OPENAI_IMAGE_HEADERS_TIMEOUT_MS, OPENAI_IMAGE_BODY_TIMEOUT_MS). The error embeds status plus the first 240 chars of the body.","triggerScenarios":"401 (bad/missing key), 402 (insufficient credits on the OpenRouter account), 404 (model slug not in the OpenRouter catalogue or not enabled for the key), 429 (rate limit), 400 (model does not support the requested modalities or the prompt exceeds limits), or upstream 5xx from the underlying provider routed through OpenRouter.","commonSituations":"Free OpenRouter key with no credits hitting a paid image model, model id with the wrong prefix, regional rate limits, or the underlying provider (e.g. Gemini-via-OpenRouter) incident surfacing as a 5xx.","solutions":["Read the embedded status+body: 401→fix key, 402→add credits, 404→check the model slug, 429→slow down, 400→check modality support.","Confirm the resolved wireModel (after stripping the openrouter/ prefix) matches a slug listed at openrouter.ai/models with image output.","Verify the OpenRouter account has credits for the chosen model tier.","If 5xx, retry with backoff or pick an alternate image model."],"exampleFix":"// before\nif (!resp.ok) {\n  throw new Error(`openrouter image ${resp.status}: ${truncate(text, 240)}`);\n}\n\n// after — actionable hints by status\nif (!resp.ok) {\n  const hint = resp.status === 401 ? ' (bad key)'\n    : resp.status === 402 ? ' (no credits)'\n    : resp.status === 404 ? ' (unknown model)'\n    : resp.status === 429 ? ' (rate limited)'\n    : '';\n  throw new Error(`openrouter image ${resp.status}${hint}: ${truncate(text, 240)}`);\n}","handlingStrategy":"try-catch","validationCode":"function describeOpenRouterImageError(status: number, body: string): string {\n  const hint = status === 401 ? 'bad key'\n    : status === 402 ? 'no credits'\n    : status === 404 ? 'unknown model'\n    : status === 429 ? 'rate limited'\n    : status >= 500 ? 'upstream provider outage — retry'\n    : 'see body';\n  return `openrouter image ${status} (${hint}): ${truncate(body, 240)}`;\n}\n\nfunction isRetryableOpenRouterStatus(status: number): boolean {\n  return status === 429 || status >= 500;\n}","typeGuard":"function isOpenRouterModelCapable(model: string): boolean {\n  // Caller should consult the OpenRouter catalogue; this is a last-mile sanity check\n  return /image|vision|gemini|dall-e|flux|seedream/i.test(model);\n}","tryCatchPattern":"for (let attempt = 0; attempt < 2; attempt++) {\n  const resp = await fetch(/* ... */);\n  if (resp.ok) { /* parse and return */ }\n  if (attempt === 0 && isRetryableOpenRouterStatus(resp.status)) {\n    await sleep(1500 * (attempt + 1));\n    continue;\n  }\n  throw new Error(describeOpenRouterImageError(resp.status, await resp.text()));\n}","preventionTips":["Classify 402 (credits) distinctly so users know to top up rather than debug.","Confirm the model slug supports image output at openrouter.ai/models.","Retry on 429/5xx with exponential backoff before surfacing.","Keep attribution headers (HTTP-Referer, X-Title) present — some models 400 without them."],"tags":["openrouter","image-generation","http-status","credentials","provider-api"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}