{"record":{"id":"bd96bfba88d29ea3","repo":"nexu-io/open-design","slug":"grok-image-response-had-no-data-0","errorCode":null,"errorMessage":"grok image response had no data[0]","messagePattern":"grok image response had no data\\[0\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":1647,"sourceCode":"    method: 'POST',\n    headers: {\n      'authorization': `Bearer ${credentials.apiKey}`,\n      'content-type': 'application/json',\n    },\n    body: JSON.stringify(body),\n  }));\n  const text = await resp.text();\n  if (!resp.ok) {\n    throw new Error(`grok image ${resp.status}: ${truncate(text, 240)}`);\n  }\n  let data: any;\n  try {\n    data = JSON.parse(text);\n  } catch {\n    throw new Error(`grok image non-JSON: ${truncate(text, 200)}`);\n  }\n  const entry = data && Array.isArray(data.data) ? data.data[0] : null;\n  if (!entry) throw new Error('grok image response had no data[0]');\n  let bytes;\n  if (entry.b64_json) {\n    bytes = Buffer.from(entry.b64_json, 'base64');\n  } else if (entry.url) {\n    const imgResp = await fetch(entry.url, withMediaRequestInit(ctx));\n    if (!imgResp.ok) throw new Error(`grok image fetch ${imgResp.status}`);\n    bytes = Buffer.from(await imgResp.arrayBuffer());\n  } else {\n    throw new Error('grok image response missing b64_json/url');\n  }\n  // xAI's Imagine returns JPEG by default (no format option in the API\n  // surface), but PNG/WebP are technically possible. Sniff the magic\n  // bytes so the on-disk extension matches reality — saving JPEG bytes\n  // as `.png` confuses Finder previews and any downstream consumer that\n  // trusts the extension.\n  return {\n    bytes,\n    providerNote: `grok/${ctx.wireModel} · ${aspectRatio} · ${bytes.length} bytes`,","sourceCodeStart":1629,"sourceCodeEnd":1665,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L1629-L1665","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the truncated upstream body the daemon logged alongside this error to see the actual envelope xAI returned.","Confirm ctx.wireModel is still a valid xAI image model id (grok-2-image family) and that the account has Imagine access.","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.","If xAI genuinely changed the schema, extend the `entry` extraction in apps/daemon/src/media/index.ts:1646 to handle the new shape.","Retry once — transient empty-result envelopes have been seen during xAI incidents."],"exampleFix":"// before\nconst entry = data && Array.isArray(data.data) ? data.data[0] : null;\nif (!entry) throw new Error('grok image response had no data[0');\n\n// after — surface the real envelope so the operator can diagnose\nconst list = data?.data;\nconst entry = Array.isArray(list) ? list[0] : null;\nif (!entry) {\n  throw new Error(\n    `grok image response had no data[0]: ${truncate(JSON.stringify(data), 200)}`,\n  );\n}","handlingStrategy":"validation","validationCode":"// Validate xAI image response shape before consuming data[0]\nfunction assertGrokImageEnvelope(data: unknown): asserts data is { data: { b64_json?: string; url?: string }[] } {\n  if (!data || typeof data !== 'object') {\n    throw new Error(`grok image: response is not an object (${typeof data})`);\n  }\n  const d = data as any;\n  if (d.error) {\n    throw new Error(`grok image upstream error: ${JSON.stringify(d.error)}`);\n  }\n  if (!Array.isArray(d.data) || d.data.length === 0) {\n    throw new Error(`grok image: data[] missing/empty; keys=${Object.keys(d).join(',')}`);\n  }\n}","typeGuard":"function isGrokImageEntry(v: unknown): v is { b64_json?: string; url?: string } {\n  return typeof v === 'object' && v !== null && (\n    typeof (v as any).b64_json === 'string' || typeof (v as any).url === 'string'\n  );\n}","tryCatchPattern":"try {\n  const entry = assertGrokImageEnvelope(data) && data.data[0];\n  // ...\n} catch (e) {\n  // log full `data` once, then surface a trimmed error to the user\n  ctx.onProviderRequestSettled?.({ providerId: 'grok', ok: false, error: String(e) });\n  throw e;\n}","preventionTips":["Always assert the envelope shape before indexing data[0]; never trust the array exists.","Log the full upstream body once when extraction fails so future shape changes are diagnosable.","Treat a 200 with an `error` field as a failure rather than falling through to data[0].","Pin ctx.wireModel to a known image-capable xAI slug and validate it in the provider registry."],"tags":["grok","xai","image-generation","response-shape","provider-api"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}