nexu-io/open-design · error · Error

grok image response had no data[0]

Error message

grok image response had no data[0]

What it means

Thrown by renderGrokImage after a successful xAI /v1/images/generations call when the parsed JSON lacks a usable first entry. The code computes `entry = data.data[0]` only when `data.data` is an Array; anything else (an object, a string, or a missing field) yields null and trips this guard. It signals an upstream response-shape contract violation rather than a network or auth failure.

Solutions

  1. Inspect the truncated upstream body the daemon logged alongside this error to see the actual envelope xAI returned.
  2. Confirm ctx.wireModel is still a valid xAI image model id (grok-2-image family) and that the account has Imagine access.
  3. If a proxy is set in credentials.baseUrl, point it back at https://api.x.ai/v1 or update the proxy to pass the response through unchanged.
  4. If xAI genuinely changed the schema, extend the `entry` extraction in apps/daemon/src/media/index.ts:1646 to handle the new shape.
  5. Retry once — transient empty-result envelopes have been seen during xAI incidents.

Example fix

// before
const entry = data && Array.isArray(data.data) ? data.data[0] : null;
if (!entry) throw new Error('grok image response had no data[0');

// after — surface the real envelope so the operator can diagnose
const list = data?.data;
const entry = Array.isArray(list) ? list[0] : null;
if (!entry) {
  throw new Error(
    `grok image response had no data[0]: ${truncate(JSON.stringify(data), 200)}`,
  );
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate xAI image response shape before consuming data[0]
function assertGrokImageEnvelope(data: unknown): asserts data is { data: { b64_json?: string; url?: string }[] } {
  if (!data || typeof data !== 'object') {
    throw new Error(`grok image: response is not an object (${typeof data})`);
  }
  const d = data as any;
  if (d.error) {
    throw new Error(`grok image upstream error: ${JSON.stringify(d.error)}`);
  }
  if (!Array.isArray(d.data) || d.data.length === 0) {
    throw new Error(`grok image: data[] missing/empty; keys=${Object.keys(d).join(',')}`);
  }
}

Type guard

function isGrokImageEntry(v: unknown): v is { b64_json?: string; url?: string } {
  return typeof v === 'object' && v !== null && (
    typeof (v as any).b64_json === 'string' || typeof (v as any).url === 'string'
  );
}

Try / catch

try {
  const entry = assertGrokImageEnvelope(data) && data.data[0];
  // ...
} catch (e) {
  // log full `data` once, then surface a trimmed error to the user
  ctx.onProviderRequestSettled?.({ providerId: 'grok', ok: false, error: String(e) });
  throw e;
}

Prevention

When it happens

Trigger: xAI returns a 200 whose body is an error envelope (e.g. `{error: {...}}`) instead of the OpenAI-style `{data:[...]}`, returns `data` as an object instead of an array, returns an empty `data: []` (n=0 effective), or xAI ships a new response schema that drops the `data` array wrapper.

Common situations: Hitting the Grok Imagine endpoint with a model slug xAI deprecated/renamed (200 with a soft error body), pointing credentials.baseUrl at a non-xAI proxy that re-shapes responses, or xAI A/B-testing a new envelope during a launch window.

Related errors


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

Appendix: source

Thrown at apps/daemon/src/media/index.ts:1647

    method: 'POST',
    headers: {
      'authorization': `Bearer ${credentials.apiKey}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify(body),
  }));
  const text = await resp.text();
  if (!resp.ok) {
    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`,

View on GitHub (pinned to 5be4028344)