{"record":{"id":"7db8cfa5b81922fa","repo":"nexu-io/open-design","slug":"aihubmix-gemini-image-response-had-no-inline-image","errorCode":null,"errorMessage":"aihubmix gemini image response had no inline image data: ${JSON.stringify(data).slice(0, 200)}","messagePattern":"aihubmix gemini image response had no inline image data: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/integrations/aihubmix.ts","lineNumber":153,"sourceCode":"    body: JSON.stringify({\n      contents: [{ role: 'user', parts: [{ text: req.prompt }] }],\n      generationConfig: {\n        responseModalities: ['TEXT', 'IMAGE'],\n        imageConfig: { aspectRatio: req.aspect },\n      },\n    }),\n  });\n  if (!resp.ok) {\n    const text = await resp.text().catch(() => '');\n    throw new Error(`aihubmix image (gemini) ${resp.status}: ${text.slice(0, 240)}`);\n  }\n  const data = (await resp.json()) as any;\n  const parts: any[] = data?.candidates?.[0]?.content?.parts ?? [];\n  const b64 = parts\n    .map((p) => p?.inlineData?.data || p?.inline_data?.data)\n    .find((d) => typeof d === 'string' && d);\n  if (!b64) {\n    throw new Error(\n      `aihubmix gemini image response had no inline image data: ${JSON.stringify(data).slice(0, 200)}`,\n    );\n  }\n  return Buffer.from(b64, 'base64');\n}\n\n// Catalogue ids vs wire names. The media registry requires globally-unique\n// model ids, but `gpt-image-1` / `dall-e-3` / `tts-1` are already owned by the\n// `openai` provider. So AIHubMix's models are registered with an `aihubmix-`\n// prefix and mapped back to the real upstream name here. A plain prefix strip\n// is the fallback so adding a new `aihubmix-<wire>` entry needs no edit here.\nconst AIHUBMIX_WIRE_MODELS: Record<string, string> = {\n  'aihubmix-gpt-image-1': 'gpt-image-1',\n  'aihubmix-dall-e-3': 'dall-e-3',\n  'aihubmix-tts-1': 'tts-1',\n};\n\nexport function aihubmixWireModel(catalogId: string): string {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/integrations/aihubmix.ts#L135-L171","documentation":"Plain Error thrown by aihubmixGeminiImageBytes when the HTTP response was OK (2xx) but the parsed JSON contains no inline image data — specifically, none of the candidate parts contain inlineData.data / inline_data.data as a non-empty string. The error echoes the first 200 chars of the JSON for debugging. This indicates the model accepted the request but returned text-only or empty content instead of an image.","triggerScenarios":"The gemini/imagen model returned 200 with only text parts (e.g. a safety refusal, a 'cannot generate' message, or a text explanation); responseModalities did not include IMAGE on the upstream side; the model safety filter blocked the prompt and returned text instead; AIHubMix stripped the image data; an unexpected response schema variant where image data lives under a different key.","commonSituations":"Prompt tripped Gemini's safety filters (returns text refusal with 200); model is text-only despite being routed here; aspectRatio/prompt combination the model declined; AIHubMix passed through a text-only response from an upstream error; a model that requires a different image-output configuration.","solutions":["Inspect the interpolated JSON (first 200 chars) — a safety refusal is usually visible as text content.","Rephrase the prompt to avoid flagged content; try a different aspectRatio.","Confirm the wireModel actually supports image output (some gemini variants are text-only).","Verify generationConfig.responseModalities includes IMAGE and the model honors it.","If the JSON shape looks valid but image data is under a different key, the parser may need updating — file an issue with the response shape."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasInlineImage(data: unknown): boolean {\n  const parts = (data as any)?.candidates?.[0]?.content?.parts ?? [];\n  return parts.some((p: any) =>\n    typeof (p?.inlineData?.data ?? p?.inline_data?.data) === 'string'\n    && (p.inlineData?.data ?? p.inline_data?.data).length > 0);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await aihubmixGeminiImageBytes(req, doFetch);\n} catch (err) {\n  if (err instanceof Error && /no inline image data/.test(err.message)) {\n    // 200 but text-only response — usually a safety refusal or wrong model\n    throw new ImageGenerationRefusedError('Model returned no image (possible safety filter or text-only model)');\n  }\n  throw err;\n}","preventionTips":["Rephrase prompts that trip Gemini safety filters.","Verify the wireModel supports image output (some gemini variants are text-only).","Confirm responseModalities includes IMAGE on the upstream config.","Inspect the echoed JSON (first 200 chars) to distinguish refusal from schema drift."],"tags":["aihubmix","byok","gemini","image-generation","safety-filter","response-parse"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}