{"record":{"id":"a84efcbf6bc7322b","repo":"garrytan/gstack","slug":"no-image-data-in-threaded-response","errorCode":null,"errorMessage":"No image data in threaded response","messagePattern":"No image data in threaded response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"design/src/iterate.ts","lineNumber":119,"sourceCode":"    });\n\n    if (!response.ok) {\n      const error = await response.text();\n      if (response.status === 403 && error.includes(\"organization must be verified\")) {\n        throw new Error(\n          \"OpenAI organization verification required.\\n\"\n          + \"Go to https://platform.openai.com/settings/organization to verify.\\n\"\n          + \"After verification, wait up to 15 minutes for access to propagate.\",\n        );\n      }\n      throw new Error(`API error (${response.status}): ${error.slice(0, 300)}`);\n    }\n\n    const data = await response.json() as any;\n    const imageItem = data.output?.find((item: any) => item.type === \"image_generation_call\");\n\n    if (!imageItem?.result) {\n      throw new Error(\"No image data in threaded response\");\n    }\n\n    return { responseId: data.id, imageData: imageItem.result };\n  } finally {\n    clearTimeout(timeout);\n  }\n}\n\nasync function callFresh(\n  apiKey: string,\n  prompt: string,\n): Promise<{ responseId: string; imageData: string }> {\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), 240_000);\n\n  try {\n    const response = await fetch(\"https://api.openai.com/v1/responses\", {\n      method: \"POST\",","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/design/src/iterate.ts#L101-L137","documentation":"Thrown by callThreaded() when OpenAI returned HTTP 200 from /v1/responses but the output array has no item of type 'image_generation_call' with a truthy .result. It is a shape contract failure: a successful status code did not carry the image bytes the caller needs, so the function refuses to return an empty/undefined payload. Common because the Responses API can legally return text/reasoning without invoking the image tool.","triggerScenarios":"200 OK where data.output either is missing, empty, contains only reasoning/text items, or contains an image_generation_call whose .result is null/empty (e.g. tool was skipped, content filter suppressed output, or model returned a refusal). Also if an API schema revision renames 'image_generation_call' or nests .result differently.","commonSituations":"Prompt tripped OpenAI's content filter so the image tool was not invoked; feedback text asked for disallowed content and the model refused; a thread that previously produced images switched to text-only on a follow-up; gpt-image-2 model name deprecated/renamed upstream; partial response due to max_output_tokens cutoff.","solutions":["Inspect data.output fully (log the JSON) to see which items were returned — text/refusal items explain the missing image.","If the model refused, adjust the feedback/brief to avoid policy-flagged content and retry.","If output is unexpectedly empty, retry as a fresh call (callFresh) in case the thread state is corrupt.","Confirm the tool config {type:'image_generation', model:'gpt-image-2'} still matches OpenAI's current schema; update if renamed.","Add max_output_tokens or other tool-invocation parameters if the model is truncating before the image call."],"exampleFix":"// before\nconst data = await response.json() as any;\nconst imageItem = data.output?.find((item: any) => item.type === \"image_generation_call\");\nif (!imageItem?.result) throw new Error(\"No image data in threaded response\");\n\n// after — capture why the image is missing for diagnostics\nconst data = await response.json() as any;\nconst imageItem = data.output?.find((item: any) => item.type === \"image_generation_call\");\nif (!imageItem?.result) {\n  const reasons = (data.output ?? []).map((i: any) => `${i.type}:${i.status ?? 'n/a'}`).join(', ');\n  throw new Error(`No image data in threaded response (output items: ${reasons || 'none'})`);\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":"function hasImageResult(data: any): boolean {\n  return Array.isArray(data?.output) &&\n    data.output.some((i: any) => i?.type === 'image_generation_call' && typeof i?.result === 'string' && i.result.length > 0);\n}","tryCatchPattern":"try {\n  return await callThreaded(apiKey, prevId, feedback);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('No image data in threaded response')) {\n    // Thread produced no image — retry fresh instead of threading\n    return callFresh(apiKey, buildAccumulatedPrompt(brief, feedback));\n  }\n  throw e;\n}","preventionTips":["Sanitize feedback to avoid content-filter trip words before sending.","Retry once via callFresh when a threaded call yields no image.","Log data.output shape when this fires to catch schema drift early.","Pin to a known-good gpt-image-2 schema and update on OpenAI release notes."],"tags":["openai-api","image-generation","response-shape","content-filter"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}