nexu-io/open-design · error · Error
aihubmix image (gemini) ${resp.status}: ${text.slice(0, 240)
Error message
aihubmix image (gemini) ${resp.status}: ${text.slice(0, 240)} What it means
Plain Error thrown by aihubmixGeminiImageBytes when the AIHubMix Gemini-native generateContent endpoint returns a non-OK HTTP status. The error includes the status code and the first 240 chars of the response body so the upstream error (auth failure, quota, model unavailable, malformed request) is visible. This is the AIHubMix BYOK image-generation path for gemini/imagen-family models that reject the OpenAI /images/generations shape.
Source
Thrown at apps/daemon/src/integrations/aihubmix.ts:145
const resp = await doFetch(url, {
method: 'POST',
redirect: 'error',
headers: {
'content-type': 'application/json',
'x-goog-api-key': req.apiKey,
...aihubmixAppCodeHeader(),
},
body: JSON.stringify({
contents: [{ role: 'user', parts: [{ text: req.prompt }] }],
generationConfig: {
responseModalities: ['TEXT', 'IMAGE'],
imageConfig: { aspectRatio: req.aspect },
},
}),
});
if (!resp.ok) {
const text = await resp.text().catch(() => '');
throw new Error(`aihubmix image (gemini) ${resp.status}: ${text.slice(0, 240)}`);
}
const data = (await resp.json()) as any;
const parts: any[] = data?.candidates?.[0]?.content?.parts ?? [];
const b64 = parts
.map((p) => p?.inlineData?.data || p?.inline_data?.data)
.find((d) => typeof d === 'string' && d);
if (!b64) {
throw new Error(
`aihubmix gemini image response had no inline image data: ${JSON.stringify(data).slice(0, 200)}`,
);
}
return Buffer.from(b64, 'base64');
}
// Catalogue ids vs wire names. The media registry requires globally-unique
// model ids, but `gpt-image-1` / `dall-e-3` / `tts-1` are already owned by the
// `openai` provider. So AIHubMix's models are registered with an `aihubmix-`
// prefix and mapped back to the real upstream name here. A plain prefix stripView on GitHub (pinned to 5be4028344)
Solutions
- Read the interpolated status + body text: 401/403 → fix API key, 429 → wait/upgrade quota, 400 → check aspectRatio and model image capability, 404 → fix wireModel.
- Verify the AIHubMix key is valid at aihubmix.com and has Gemini image access on the plan.
- Confirm the wireModel resolves to a real gemini/imagen image model (use the aihubmix- prefix mapping).
- Check the aspect ratio string ('1:1','16:9', etc.) is supported by the target model.
- Retry transient 5xx after a brief backoff; persistent failure points at config.
Defensive patterns
Strategy: retry
Validate before calling
async function aihubmixKeyValid(baseUrl: string, apiKey: string): Promise<boolean> {
try {
const r = await fetch(`${baseUrl.replace(/\/$/, '')}/models`, { headers: { authorization: `Bearer ${apiKey}` } });
return r.ok;
} catch { return false; }
} Type guard
function isAihubmixImageHttpError(err: unknown): boolean {
return err instanceof Error && /^aihubmix image \(gemini\) \d{3}:/.test(err.message);
}
function aihubmixStatus(err: unknown): number | null {
const m = err instanceof Error ? err.message.match(/^aihubmix image \(gemini\) (\d{3}):/) : null;
return m ? Number(m[1]) : null;
} Try / catch
try {
return await aihubmixGeminiImageBytes(req, doFetch);
} catch (err) {
const status = aihubmixStatus(err);
if (status && (status === 429 || status >= 500)) {
// transient — retry with backoff
return await retryWithBackoff(() => aihubmixGeminiImageBytes(req, doFetch));
}
if (status === 401 || status === 403) throw new InvalidApiKeyError('AIHubMix key rejected');
throw err;
} Prevention
- Validate the AIHubMix key + plan covers gemini image models before the first image call.
- Confirm the wireModel is an image-capable gemini/imagen variant.
- Use a supported aspectRatio string for the target model.
- Retry only 429/5xx; auth and 400-class errors are not transient.
When it happens
Trigger: Any non-2xx from POST {origin}/gemini/v1beta/models/{model}:generateContent: 401/403 (bad/revoked API key), 429 (rate limit / quota), 400 (unsupported aspect ratio, model does not support image output, malformed prompt), 404 (wrong wireModel name), 5xx (AIHubMix or upstream Gemini outage). The fetch uses redirect:'error' so a 3xx redirect also surfaces here.
Common situations: Expired or quota-exhausted AIHubMix API key; wrong wireModel mapping (e.g. asking a text-only gemini model for image output); aspect ratio string the model rejects; AIHubMix gateway outage; APP-Code header stripped by a proxy; the gemini model not enabled on the user's AIHubMix plan.
Related errors
- aihubmix gemini image response had no inline image data: ${J
- nano-banana image non-JSON: ${truncate(text, 200)}
- Cloudflare returned a non-JSON response.
- Vercel returned a non-JSON response.
- UPSTREAM_UNAVAILABLE
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/75a2b3512f097d8a.
Report an issue: GitHub.