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 strip

View on GitHub (pinned to 5be4028344)

Solutions

  1. 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.
  2. Verify the AIHubMix key is valid at aihubmix.com and has Gemini image access on the plan.
  3. Confirm the wireModel resolves to a real gemini/imagen image model (use the aihubmix- prefix mapping).
  4. Check the aspect ratio string ('1:1','16:9', etc.) is supported by the target model.
  5. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/75a2b3512f097d8a. Report an issue: GitHub.