nexu-io/open-design · error · Error
grok image response missing b64_json/url
Error message
grok image response missing b64_json/url
What it means
Thrown by renderGrokImage when `data.data[0]` exists but carries neither `b64_json` nor `url` — i.e. the entry object has an unexpected shape. It is the final defensive guard after the b64 and URL branches, catching upstream field renames or partial responses.
Source
Thrown at apps/daemon/src/media/index.ts:1656
throw new Error(`grok image ${resp.status}: ${truncate(text, 240)}`);
}
let data: any;
try {
data = JSON.parse(text);
} catch {
throw new Error(`grok image non-JSON: ${truncate(text, 200)}`);
}
const entry = data && Array.isArray(data.data) ? data.data[0] : null;
if (!entry) throw new Error('grok image response had no data[0]');
let bytes;
if (entry.b64_json) {
bytes = Buffer.from(entry.b64_json, 'base64');
} else if (entry.url) {
const imgResp = await fetch(entry.url, withMediaRequestInit(ctx));
if (!imgResp.ok) throw new Error(`grok image fetch ${imgResp.status}`);
bytes = Buffer.from(await imgResp.arrayBuffer());
} else {
throw new Error('grok image response missing b64_json/url');
}
// xAI's Imagine returns JPEG by default (no format option in the API
// surface), but PNG/WebP are technically possible. Sniff the magic
// bytes so the on-disk extension matches reality — saving JPEG bytes
// as `.png` confuses Finder previews and any downstream consumer that
// trusts the extension.
return {
bytes,
providerNote: `grok/${ctx.wireModel} · ${aspectRatio} · ${bytes.length} bytes`,
suggestedExt: sniffImageExt(bytes),
};
}
async function renderNanoBananaImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
const apiKey = credentials.apiKey;
if (!apiKey) {
throw new Error(
'no Nano Banana API key — configure it in Settings or set OD_NANOBANANA_API_KEY',View on GitHub (pinned to 5be4028344)
Solutions
- Log/inspect the full `entry` object — it usually reveals either a moderation refusal or a renamed field.
- If the entry is a moderation refusal, reword ctx.prompt and retry.
- Confirm ctx.wireModel is an image-capable xAI model, not a text/chat model.
- If xAI renamed the field, extend the two-branch selector in apps/daemon/src/media/index.ts:1649-1656 to read the new key.
Example fix
// before
if (entry.b64_json) {
bytes = Buffer.from(entry.b64_json, 'base64');
} else if (entry.url) {
// ...
} else {
throw new Error('grok image response missing b64_json/url');
}
// after — expose the offending entry
throw new Error(
`grok image response missing b64_json/url: ${truncate(JSON.stringify(entry), 200)}`,
); Defensive patterns
Strategy: type-guard
Validate before calling
// Detect moderation refusals before throwing the generic missing-field error
function extractGrokImageBytes(entry: any): Buffer {
if (entry.b64_json) return Buffer.from(entry.b64_json, 'base64');
if (typeof entry.url === 'string') throw new Error(`grok image requires url fetch: ${entry.url}`);
if (entry.prompt_feedback || entry.revised_prompt || entry.error) {
throw new Error(`grok image refused/revised: ${JSON.stringify(entry).slice(0, 200)}`);
}
throw new Error(`grok image entry shape unrecognized: ${JSON.stringify(entry).slice(0, 200)}`);
} Type guard
interface GrokImageEntry { b64_json?: string; url?: string; revised_prompt?: string; prompt_feedback?: unknown; error?: unknown }
function isGrokImageEntry(v: unknown): v is GrokImageEntry {
return typeof v === 'object' && v !== null;
} Try / catch
try {
bytes = extractGrokImageBytes(entry);
} catch (e) {
ctx.onProviderRequestSettled?.({ providerId: 'grok', ok: false, error: String(e) });
throw e;
} Prevention
- Branch on the actual field names xAI uses; do not assume only b64_json/url exist.
- Detect and label moderation/refusal entries distinctly from shape errors so operators know to reword the prompt.
- Log the unrecognized entry keys when extraction fails.
- Confirm the model is image-capable before sending.
When it happens
Trigger: xAI returns `data:[{revised_prompt:...}]` with the image payload under a new field name, returns a metadata-only entry, or returns a content-moderation refusal object that omits the image fields entirely.
Common situations: Prompt triggers xAI safety filters and the entry contains only a `prompt_feedback`/`revision` object; xAI ships a revised API that renames b64_json; the model variant selected does not actually produce images.
Related errors
- grok image response had no data[0]
- grok image fetch ${imgResp.status}
- nano-banana image response missing candidates[].content.part
- openrouter image response contained no images for model ${wi
- nano-banana image non-JSON: ${truncate(text, 200)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/9f41047c98754d4a.
Report an issue: GitHub.