moeru-ai/airi · error · Error

Responses image output requires a URL

Error message

Responses image output requires a URL

What it means

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.

Solutions

  1. Set image_url on every input_image part in the tool's function_call_output content (a valid http(s) or data: URL).
  2. If the tool only has image bytes, convert them to a data URL (e.g. `data:image/png;base64,...`) before returning them.
  3. Drop the image part and return only text if no URL can be produced.
  4. Log/inspect the raw tool output to find which code path emits input_image without image_url and fix that tool implementation.

Example fix

// before
{ type: 'input_image', detail: 'auto' } // image_url missing

// after
{ type: 'input_image', image_url: 'data:image/png;base64,iVBORw0...', detail: 'auto' }
Defensive patterns

Strategy: validation

Validate before calling

function assertImagePartsHaveUrls(output) {
  for (const part of (Array.isArray(output) ? output : [])) {
    if (part.type === 'input_image' && !part.image_url)
      throw new Error('tool output image part missing image_url')
  }
}

Type guard

const hasImageSource = (part) => part.type !== 'input_image' || (typeof part.image_url === 'string' && part.image_url.length > 0)

Try / catch

try {
  await streamResponses({ config, conversation, scope, onEvent })
} catch (e) {
  if (e.message === 'Responses image output requires a URL') {
    // sanitize the tool output (drop image parts or supply data URLs) and retry
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17). Data as JSON: /api/errors/f0b4a670af421b13. Report an issue: GitHub.

Appendix: source

Thrown at packages/core-agent/src/runtime/responses.ts:100

      if (continuation?.protocol === 'responses' && continuation.scope === scope) {
        if (!Array.isArray(continuation.data))
          throw new Error('Responses continuation must contain an item array')
        return continuation.data
      }
      return projectRound(round).flatMap(renderMessage)
    })
  })
}

function readToolResultContent(content: Extract<ItemParam, { type: 'function_call_output' }>['output']): InputSegment[] {
  if (typeof content === 'string')
    return [{ type: 'text', text: content }]
  return content.map((part) => {
    switch (part.type) {
      case 'input_text': return { type: 'text', text: part.text }
      case 'input_image':
        if (!part.image_url)
          throw new Error('Responses image output requires a URL')
        return { type: 'image', url: part.image_url, detail: part.detail ?? undefined }
      case 'input_file':
        if (part.file_data != null && part.file_url == null)
          return { type: 'file', data: part.file_data, name: part.filename ?? undefined }
        if (part.file_url != null && part.file_data == null)
          return { type: 'file', url: part.file_url, name: part.filename ?? undefined }
        throw new Error('Responses file requires exactly one source')
      case 'input_video': throw new Error('Video tool output is not supported by the conversation model')
    }
    throw new Error('Unsupported Responses tool output')
  })
}

type AssistantContent = Exclude<Extract<ItemParam, { role: 'assistant' }>['content'], string>[number]

function readCitations(part: Extract<AssistantContent, { type: 'output_text' }>): Citation[] | undefined {
  return part.annotations?.map(entry => ({
    url: entry.url,

View on GitHub (pinned to 438a067dde)