{"record":{"id":"f0b4a670af421b13","repo":"moeru-ai/airi","slug":"responses-image-output-requires-a-url","errorCode":null,"errorMessage":"Responses image output requires a URL","messagePattern":"Responses image output requires a URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core-agent/src/runtime/responses.ts","lineNumber":100,"sourceCode":"      if (continuation?.protocol === 'responses' && continuation.scope === scope) {\n        if (!Array.isArray(continuation.data))\n          throw new Error('Responses continuation must contain an item array')\n        return continuation.data\n      }\n      return projectRound(round).flatMap(renderMessage)\n    })\n  })\n}\n\nfunction readToolResultContent(content: Extract<ItemParam, { type: 'function_call_output' }>['output']): InputSegment[] {\n  if (typeof content === 'string')\n    return [{ type: 'text', text: content }]\n  return content.map((part) => {\n    switch (part.type) {\n      case 'input_text': return { type: 'text', text: part.text }\n      case 'input_image':\n        if (!part.image_url)\n          throw new Error('Responses image output requires a URL')\n        return { type: 'image', url: part.image_url, detail: part.detail ?? undefined }\n      case 'input_file':\n        if (part.file_data != null && part.file_url == null)\n          return { type: 'file', data: part.file_data, name: part.filename ?? undefined }\n        if (part.file_url != null && part.file_data == null)\n          return { type: 'file', url: part.file_url, name: part.filename ?? undefined }\n        throw new Error('Responses file requires exactly one source')\n      case 'input_video': throw new Error('Video tool output is not supported by the conversation model')\n    }\n    throw new Error('Unsupported Responses tool output')\n  })\n}\n\ntype AssistantContent = Exclude<Extract<ItemParam, { role: 'assistant' }>['content'], string>[number]\n\nfunction readCitations(part: Extract<AssistantContent, { type: 'output_text' }>): Citation[] | undefined {\n  return part.annotations?.map(entry => ({\n    url: entry.url,","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/core-agent/src/runtime/responses.ts#L82-L118","documentation":"While reading a `function_call_output` item back into the portable conversation model, an `input_image` part lacks `image_url`. The adapter maps tool-output images to an image segment with a URL; without a URL there is no way to represent the image downstream, so it throws.","triggerScenarios":"A tool returns function_call_output content containing `{ type: 'input_image' }` with image_url null/undefined/empty — e.g. the tool's response builder omitted image_url, or upstream API stripping produced a part without a URL.","commonSituations":"Writing custom tools whose output includes images assembled by hand; a provider change that renames or drops the image_url field; tools returning base64-only images where the adapter expects a URL.","solutions":["Set image_url on every input_image part in the tool's function_call_output content (a valid http(s) or data: URL).","If the tool only has image bytes, convert them to a data URL (e.g. `data:image/png;base64,...`) before returning them.","Drop the image part and return only text if no URL can be produced.","Log/inspect the raw tool output to find which code path emits input_image without image_url and fix that tool implementation."],"exampleFix":"// before\n{ type: 'input_image', detail: 'auto' } // image_url missing\n\n// after\n{ type: 'input_image', image_url: 'data:image/png;base64,iVBORw0...', detail: 'auto' }","handlingStrategy":"validation","validationCode":"function assertImagePartsHaveUrls(output) {\n  for (const part of (Array.isArray(output) ? output : [])) {\n    if (part.type === 'input_image' && !part.image_url)\n      throw new Error('tool output image part missing image_url')\n  }\n}","typeGuard":"const hasImageSource = (part) => part.type !== 'input_image' || (typeof part.image_url === 'string' && part.image_url.length > 0)","tryCatchPattern":"try {\n  await streamResponses({ config, conversation, scope, onEvent })\n} catch (e) {\n  if (e.message === 'Responses image output requires a URL') {\n    // sanitize the tool output (drop image parts or supply data URLs) and retry\n  } else throw e\n}","preventionTips":["In tool wrappers, convert raw image bytes to data: URLs before building input_image parts.","Validate tool output parts against the expected input_file/input_image/input_text shapes before returning them from the tool.","Add tests for every tool that returns non-text output."],"tags":["responses-api","tool-output","images","missing-field"],"backgroundTag":"missing-required-argument","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-09-17T01:14:42.644Z","contentChangedAt":"2026-09-17T01:14:42.644Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}