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
- 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.
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
- 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.
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
- Responses file requires exactly one source
- Only assistant messages can invoke tools
- Responses continuation must contain an item array
- Tool messages require a correlated tool result
- Unsupported Responses assistant content
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)