moeru-ai/airi · error · Error

Unsupported Responses tool output

Error message

Unsupported Responses tool output

What it means

readToolResultContent switches over the part.type of Responses function_call_output parts. This fallback throw fires when a part's type matches none of the known cases (input_text, input_image, input_file, input_video). It guards against unknown or future OpenAI Responses content types the adapter cannot project into the portable conversation model.

Solutions

  1. Identify the offending part.type from the conversation/continuation data and convert it to a supported type (input_text) before storing.
  2. Upgrade @proj-airi/core-agent / the Responses adapter to a version that knows the new content part type.
  3. As a workaround, sanitize function_call_output parts with a whitelist filter before pushing them into the conversation.

Example fix

// before
items.push({ type: 'function_call_output', call_id: segment.callId, output: content })

// after
const allowed = ['input_text', 'input_image', 'input_file']
items.push({ type: 'function_call_output', call_id: segment.callId, output: content.filter(p => allowed.includes(p.type)) })
Defensive patterns

Strategy: type-guard

Validate before calling

const KNOWN = new Set(['input_text', 'input_image', 'input_file', 'input_video'])
const unknown = output.filter(p => !KNOWN.has(p?.type))
if (unknown.length) console.warn('unknown tool output part types:', unknown.map(p => p.type))

Type guard

function isKnownToolOutputPart(part) {
  return ['input_text', 'input_image', 'input_file', 'input_video'].includes(part?.type)
}

Try / catch

try {
  projection = readOutput(items)
} catch (err) {
  if (err.message === 'Unsupported Responses tool output') {
    console.error('Unrecognized function_call_output part type; upgrade adapter or sanitize parts.')
  } else throw err
}

Prevention

When it happens

Trigger: readOutput() -> readToolResultContent() receives a function_call_output whose output is an array containing a part whose type is not input_text/input_image/input_file/input_video — e.g. a newer API content type, a typo like 'input_texts', or a hand-constructed part object.

Common situations: Upstream Responses API added a new content part type after this adapter was written; a plugin or tool writes non-standard parts into conversation state; a persisted continuation from a newer library version is read by an older adapter.

Related errors


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

Appendix: source

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

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,
    title: entry.title,
    startIndex: entry.start_index,
    endIndex: entry.end_index,
  }))
}

function readOutput(items: ItemParam[]): ProjectionEntry[] {
  return items.flatMap<ProjectionEntry>((item, index) => {
    const id = `output-${index}`
    if (item.type === 'function_call')

View on GitHub (pinned to 438a067dde)