HeyPuter/puter · error · HttpError
unknown_error
unknown_error
Error message
Failed to extract image URL from Gemini response
What it means
Thrown by GeminiImageProvider.generate() (GeminiImageProvider.ts:262) after a successful generateContent API call when #extractImageUrl() returns undefined. That helper walks response.candidates[0].content.parts looking for a part with inlineData.data; if no such part exists (no image modality in the response), the provider cannot return an image. Note billing has already been recorded at this point. HTTP 400 with legacyCode unknown_error — the response shape was unexpected.
Source
Thrown at src/backend/drivers/ai-image/providers/gemini/GeminiImageProvider.ts:262
usageAmount: Math.max(inputTokenCount, 1),
costOverride: this.#toMicroCents(inputCostInCents),
},
{
usageType: `${usagePrefix}:output:text`,
usageAmount: Math.max(outputTextTokenCount, 1),
costOverride: this.#toMicroCents(outputTextCostInCents),
},
{
usageType: `${usagePrefix}:output:image`,
usageAmount: Math.max(outputImageTokenCount, 1),
costOverride: this.#toMicroCents(outputImageCostInCents),
},
]);
const url = this.#extractImageUrl(response);
if (!url) {
throw new HttpError(
400,
'Failed to extract image URL from Gemini response',
{ legacyCode: 'unknown_error' },
);
}
return url;
}
async #generateWithImagen(
prompt: string,
selectedModel: IGeminiImageModel,
params: IGenerateParams,
): Promise<string> {
const actor = Context.get('actor');
if (!actor) {
throw new HttpError(401, 'actor not found in context', {
legacyCode: 'unauthorized',View on GitHub (pinned to 908ec23eda)
Solutions
- Rephrase the prompt to avoid content likely to trip safety filters (violence, explicit, copyright, public figures).
- Retry the request — transient non-image responses can occur; if it persists, the prompt/model is the issue.
- Inspect the full Gemini response (candidates[0].content.parts, finishReason, safetyRatings) in logs to see why no image was returned.
- If using a preview model, switch to a stable model id or check for API deprecation.
Defensive patterns
Strategy: retry
Try / catch
let lastErr;
for (let attempt = 0; attempt < 2; attempt++) {
try {
return await provider.generate(params);
} catch (e) {
lastErr = e;
if (e instanceof HttpError && e.status_code === 400 && e.message.includes('extract image URL')) {
continue; // retry once for transient non-image responses
}
throw e;
}
}
throw lastErr; Prevention
- Avoid prompts likely to trip Gemini safety filters.
- Log the full response when this occurs to diagnose whether it is filtering or an API change.
- Prefer stable model ids over preview models to reduce response-shape drift.
When it happens
Trigger: Gemini returned a text-only response (image generation implicitly declined); content was filtered by Gemini's safety settings without an explicit error; the model returned candidates with no parts or parts without inlineData; an API version change altered the response envelope.
Common situations: Prompt tripped Gemini's safety filters (violence, NSFW, etc.) producing text refusal instead of an image; model quirk where it returns reasoning text but no image; transient provider behavior; SDK version mismatch changing response structure.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/0ec77cd713f48720.
Report an issue: GitHub.