{"record":{"id":"cc1982125d57d957","repo":"moeru-ai/airi","slug":"unsupported-chat-content-part","errorCode":null,"errorMessage":"Unsupported Chat content part","messagePattern":"Unsupported Chat content part","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core-agent/src/messages/chat-completions.ts","lineNumber":33,"sourceCode":" */\nexport function chatContentToInputSegments(content: string | CommonContentPart[] | undefined): InputSegment[] {\n  if (content == null)\n    return []\n  if (typeof content === 'string')\n    return [{ type: 'text', text: content }]\n  return content.map((part) => {\n    switch (part.type) {\n      case 'text': return { type: 'text', text: part.text }\n      case 'image_url': return { type: 'image', url: part.image_url.url, detail: part.image_url.detail }\n      case 'input_audio': return { type: 'audio', ...part.input_audio }\n      case 'file':\n        if (part.file.file_data !== undefined && part.file.file_id === undefined)\n          return { type: 'file', data: part.file.file_data, name: part.file.filename }\n        if (part.file.file_id !== undefined && part.file.file_data === undefined)\n          return { type: 'file', providerFileId: part.file.file_id, name: part.file.filename }\n        throw new Error('Chat file requires exactly one source')\n    }\n    throw new Error('Unsupported Chat content part')\n  })\n}\n\n/**\n * Reads Chat-shaped storage or SDK output into portable message semantics.\n * This is an ingress boundary; Responses never calls the Chat request renderer.\n *\n * @example\n * chatMessagesToProjectionEntries([{ role: 'user', content: 'Hello' }])[0].segments\n * // => [{ type: 'text', text: 'Hello' }]\n */\nexport function chatMessagesToProjectionEntries(messages: (ChatMessage | { role: 'error', content: string })[], idPrefix = 'message'): ProjectionEntry[] {\n  return messages.map((message, index) => {\n    const id = `${idPrefix}-${index}`\n    if (message.role === 'error')\n      return { id, role: 'user', segments: [{ type: 'text', text: `User encountered error: ${message.content}` }] }\n    if (message.role === 'tool')\n      return { id, role: 'tool', segments: [{ type: 'tool-result', callId: message.tool_call_id, content: chatContentToInputSegments(message.content) }] }","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/core-agent/src/messages/chat-completions.ts#L15-L51","documentation":"chatContentToInputSegments maps known Chat content part types (text, image_url, input_audio, file) to portable input segments. Any part whose type is outside that set has no portable representation, so the library throws rather than silently dropping or mis-typing the content.","triggerScenarios":"Calling chatContentToInputSegments (via chatMessagesToProjectionEntries or replaceToolCallResult) with a content array containing a part.type that is not 'text', 'image_url', 'input_audio', or 'file' — e.g. a newer OpenAI part type like custom tool parts or an unrecognized/misspelled type from a compatible server.","commonSituations":"Provider SDK updated and introduced new content part types this ingress does not know; a proxy/compatible server returns nonstandard part types; a typo when hand-writing parts (e.g. type: 'imageUrl'); replaying messages captured from a different API surface (Responses vs Chat).","solutions":["Inspect the content part's type at the call site and drop or convert unsupported parts before ingress","Map the unsupported part to a supported one (e.g. convert to text) if it carries equivalent information","Check the @xsai/shared-chat CommonContentPart type for the recognized set and update the library/dependency if a new part type must be supported"],"exampleFix":"// before\nparts.filter(p => p.type === 'reasoning') // later passed as-is into chatMessagesToProjectionEntries\n// after\nparts.filter(p => ['text','image_url','input_audio','file'].includes(p.type))","handlingStrategy":"type-guard","validationCode":"const SUPPORTED = new Set(['text', 'image_url', 'input_audio', 'file'])\nif (!contentParts.every(p => SUPPORTED.has(p.type))) throw new Error('Unsupported content part type before ingress')","typeGuard":"function isSupportedChatPart(part) {\n  return ['text', 'image_url', 'input_audio', 'file'].includes(part.type)\n}","tryCatchPattern":"try {\n  const segments = chatContentToInputSegments(content)\n} catch (err) {\n  if (err.message === 'Unsupported Chat content part') {\n    return content.filter(isSupportedChatPart).map(convertSafely)\n  }\n  throw err\n}","preventionTips":["Filter content arrays against the supported part-type set before calling ingress functions","Keep @xsai/shared-chat updated and check for new CommonContentPart variants after SDK upgrades","Log and drop unknown part types from compatible servers instead of passing them through","Type content arrays as CommonContentPart so typos fail at compile time"],"tags":["chat-completions","content-parts","unsupported","ingress"],"backgroundTag":"unsupported-enum-value","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"}