moeru-ai/airi · error · Error

Unsupported Responses assistant content

Error message

Unsupported Responses assistant content

What it means

readOutput projects assistant message items from a Responses output array into portable assistant entries. Within a message item, only output_text and refusal content parts are supported; any other assistant content part type triggers this throw. It protects the portable conversation model from unrepresentable content.

Solutions

  1. Upgrade the Responses adapter so it maps the new assistant content part type.
  2. Inspect the continuation/response data and normalize the assistant content to output_text/refusal before projection.
  3. If the part is ignorable, pre-filter item.content to output_text/refusal parts before calling code that projects it.

Example fix

// before
const segments = item.content.map(mapPart)

// after
const segments = item.content
  .filter(p => p.type === 'output_text' || p.type === 'refusal')
  .map(mapPart)
Defensive patterns

Strategy: type-guard

Validate before calling

const bad = item.content.filter(p => p.type !== 'output_text' && p.type !== 'refusal')
if (bad.length) console.warn('unsupported assistant content parts:', bad.map(p => p.type))

Type guard

function isProjectableAssistantPart(part) {
  return part?.type === 'output_text' || part?.type === 'refusal'
}

Try / catch

try {
  entries = readOutput(items)
} catch (err) {
  if (err.message === 'Unsupported Responses assistant content') {
    console.error('Assistant message contained an unprojectable content part; filter or upgrade adapter.')
  } else throw err
}

Prevention

When it happens

Trigger: generation() -> readOutput() processes a Responses 'message' item with role 'assistant' whose content array contains a part type other than 'output_text' or 'refusal' (e.g. a new API part type such as audio, or a malformed/typo'd type).

Common situations: A model response came back with an assistant content part type the adapter predates (API surface evolution); a mock or fixture hand-builds an assistant message with an unknown part; replaying continuation data captured from a newer API version.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

  }))
}

function readOutput(items: ItemParam[]): ProjectionEntry[] {
  return items.flatMap<ProjectionEntry>((item, index) => {
    const id = `output-${index}`
    if (item.type === 'function_call')
      return [{ id, role: 'assistant', segments: [{ type: 'tool-call', callId: item.call_id, name: item.name, arguments: item.arguments }] }]
    if (item.type === 'function_call_output')
      return [{ id, role: 'tool', segments: [{ type: 'tool-result', callId: item.call_id, content: readToolResultContent(item.output) }] }]
    if (item.type === 'message' && item.role === 'assistant') {
      const segments: Extract<ProjectionEntry, { role: 'assistant' }>['segments'] = typeof item.content === 'string'
        ? [{ type: 'text', text: item.content }]
        : item.content.map((part) => {
            if (part.type === 'output_text')
              return { type: 'text', text: part.text, citations: readCitations(part) }
            if (part.type === 'refusal')
              return { type: 'refusal', text: part.refusal }
            throw new Error('Unsupported Responses assistant content')
          })
      return [{ id, role: 'assistant', segments }]
    }
    // These native records have no portable message content. Search citations
    // already belong to the assistant text; opaque state stays in continuation.
    if (item.type === 'reasoning' || item.type === 'compaction' || item.type === 'web_search_call')
      return []
    throw new Error(`Unsupported Responses output item: ${item.type}`)
  })
}

function toolChoice(choice: StreamOptions['toolChoice']): ResponsesOptions['toolChoice'] {
  if (choice == null || typeof choice === 'string')
    return choice
  if (choice.type === 'function')
    return { type: 'function', name: choice.function.name }
  return { type: 'allowed_tools', mode: choice.mode, tools: choice.tools.map(tool => ({ type: 'function', name: tool.function.name })) }
}

View on GitHub (pinned to 438a067dde)