{"record":{"id":"29efc346fc12098d","repo":"moeru-ai/airi","slug":"responses-continuation-must-contain-an-item-array","errorCode":null,"errorMessage":"Responses continuation must contain an item array","messagePattern":"Responses continuation must contain an item array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core-agent/src/runtime/responses.ts","lineNumber":84,"sourceCode":"      items.push({ type: 'function_call_output', call_id: segment.callId, output: content.every(part => part.type === 'input_text') ? content.map(part => part.text).join('') : content })\n    }\n    else {\n      parts.push(segment)\n    }\n  }\n  flush()\n  return items\n}\n\nfunction renderConversation(conversation: Conversation, scope: string): ItemParam[] {\n  return conversation.turns.flatMap((turn) => {\n    if (turn.type !== 'assistant')\n      return renderMessage(projectInput(turn))\n    return turn.rounds.flatMap((round) => {\n      const continuation = round.continuation\n      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':","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/core-agent/src/runtime/responses.ts#L66-L102","documentation":"When rendering the conversation, an assistant round carries a continuation stamped with protocol 'responses' and matching scope, but its `data` field is not an array. Continuation data holds native Responses `ItemParam[]` from previous steps so stateless multi-step tool loops can be replayed; the renderer reuses it verbatim, so a non-array would silently corrupt the request input.","triggerScenarios":"A stored or hand-written continuation object `{ protocol: 'responses', scope: <matching scope>, data: <non-array> }` on an assistant round. Happens when persisted continuation state was written by an older version with a different data shape, when the continuation was deserialized from JSON with data replaced by an object/null, or when custom code assigned the wrong payload to continuation.data.","commonSituations":"Upgrading the runtime so old persisted conversation snapshots have a stale continuation schema; manually editing or truncating persisted conversation files; a bug in a custom generation wrapper that stores the response object instead of the item array.","solutions":["Ensure round.continuation.data is always an ItemParam[] (the raw Responses items returned by the generation step).","Delete the stale continuation from the persisted round so renderConversation falls back to projectRound + renderMessage, which re-renders the round portably.","If migrating persisted state, re-create continuations by replaying or strip all continuations (they are an optimization for stateless replay).","Verify the scope string matches how it was created (input.scope); mismatched scope entries are ignored, so if you see this, scope matched but data was wrong — check the writer of the continuation."],"exampleFix":"// before\ncontinuation: { protocol: 'responses', scope, data: { items: [...] } }\n\n// after\ncontinuation: { protocol: 'responses', scope, data: [...items] } // ItemParam[]","handlingStrategy":"type-guard","validationCode":"const usableContinuation = (round, scope) => {\n  const c = round.continuation\n  return !!c && c.protocol === 'responses' && c.scope === scope && Array.isArray(c.data) ? c : null\n}","typeGuard":"const isResponsesContinuation = (c) => c != null && typeof c === 'object' && c.protocol === 'responses' && Array.isArray(c.data)","tryCatchPattern":"try {\n  await streamResponses({ config, conversation, scope, onEvent })\n} catch (e) {\n  if (e.message === 'Responses continuation must contain an item array') {\n    // strip invalid continuations from all assistant rounds and retry\n    conversation.turns.forEach(t => t.type === 'assistant' && t.rounds.forEach(r => { delete r.continuation }))\n  } else throw e\n}","preventionTips":["Never hand-write continuation objects; only assign data returned by the generation step.","Version or validate persisted conversation snapshots on load; drop continuations with unknown shapes.","Remember continuations are a stateless-replay optimization — they are always safe to delete."],"tags":["responses-api","continuation-state","schema-mismatch","persisted-state"],"backgroundTag":"unexpected-response-shape","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"}