{"record":{"id":"3ef88038ba999506","repo":"can1357/oh-my-pi","slug":"unknown-user-content-type","errorCode":null,"errorMessage":"Unknown user content type","messagePattern":"Unknown user content type","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/amazon-bedrock.ts","lineNumber":860,"sourceCode":"\t\t\t\tif (typeof m.content === \"string\") {\n\t\t\t\t\t// Skip empty user messages\n\t\t\t\t\tif (!m.content || m.content.trim() === \"\") continue;\n\t\t\t\t\tresult.push({ role: \"user\", content: [{ text: m.content.toWellFormed() }] });\n\t\t\t\t} else {\n\t\t\t\t\tconst contentBlocks: UserContent[] = [];\n\t\t\t\t\tfor (const c of m.content) {\n\t\t\t\t\t\tswitch (c.type) {\n\t\t\t\t\t\t\tcase \"text\": {\n\t\t\t\t\t\t\t\tconst text = c.text.toWellFormed();\n\t\t\t\t\t\t\t\tif (text.trim().length === 0) continue;\n\t\t\t\t\t\t\t\tcontentBlocks.push({ text });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"image\":\n\t\t\t\t\t\t\t\tcontentBlocks.push({ image: createImageBlock(c.mimeType, c.data) });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tthrow new AIError.ValidationError(\"Unknown user content type\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Skip message if all blocks filtered out\n\t\t\t\t\tif (contentBlocks.length === 0) continue;\n\t\t\t\t\tresult.push({ role: \"user\", content: contentBlocks });\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"assistant\": {\n\t\t\t\t// Skip assistant messages with empty content (e.g., from aborted requests)\n\t\t\t\t// Bedrock rejects messages with empty content arrays\n\t\t\t\tif (m.content.length === 0) continue;\n\t\t\t\tconst contentBlocks: AssistantContent[] = [];\n\t\t\t\tfor (const c of m.content) {\n\t\t\t\t\tswitch (c.type) {\n\t\t\t\t\t\tcase \"text\":\n\t\t\t\t\t\t\t// Skip empty text blocks\n\t\t\t\t\t\t\tif (c.text.trim().length === 0) continue;\n\t\t\t\t\t\t\tcontentBlocks.push({ text: c.text.toWellFormed() });","sourceCodeStart":842,"sourceCodeEnd":878,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/amazon-bedrock.ts#L842-L878","documentation":"When converting a UserMessage into Bedrock's wire format, each content block's type is switched over. Only text, thinking-related, and image block types are supported for the user role; any other content type (e.g. tool_call, audio, document) hits the default branch and throws a ValidationError. This is a client-side pre-flight guard — nothing was sent to AWS.","triggerScenarios":"Calling the Bedrock provider with a user message whose content array contains a block type other than text/image — for example passing tool-result or tool-call content as plain user content instead of using the proper tool message path.","commonSituations":"Building message histories manually and including unsupported block types; porting conversation state from another provider whose user-role blocks (e.g. audio, documents) Bedrock conversion doesn't map; a bug in middleware that rewrites user messages.","solutions":["Inspect the user message content array and remove/convert unsupported block types before calling the provider","Use the provider's tool-result message representation for tool outputs instead of embedding them as generic user blocks","Filter or map content blocks: keep only text and image blocks (with image/jpeg, png, gif, webp MIME types) for user messages","If you need a new block type supported, extend the conversion switch in amazon-bedrock.ts rather than passing it through"],"exampleFix":"// before\nmessages.push({ role: \"user\", content: [{ type: \"document\", mediaType: \"application/pdf\", data }] });\n// after: send only supported user blocks\nmessages.push({\n  role: \"user\",\n  content: [{ type: \"text\", text: \"See attached notes\" }],\n});\n// or supply tool output via the tool-result message role","handlingStrategy":"validation","validationCode":"const USER_BLOCK_TYPES = new Set([\"text\", \"image\"]);\nfunction hasOnlySupportedUserBlocks(msg: Context): boolean {\n  return msg.role !== \"user\" || msg.content.every(c => USER_BLOCK_TYPES.has(c.type));\n}\n// run over all user messages before calling the provider\nconst bad = context.find(m => !hasOnlySupportedUserBlocks(m));\nif (bad) throw new Error(`User message contains unsupported block type: ${bad.content.map(c => c.type).join(\",\")}`);","typeGuard":"function isSupportedUserBlock(c: ContentBlock): c is TextBlock | ImageBlock {\n  return c.type === \"text\" || c.type === \"image\";\n}","tryCatchPattern":"try {\n  await provider.stream(context);\n} catch (err) {\n  if (err instanceof AIError.ValidationError && err.message === \"Unknown user content type\") {\n    // sanitize and retry once with text/image blocks only\n    return provider.stream(context.map(sanitizeUserMessage));\n  }\n  throw err;\n}","preventionTips":["Build user messages only from text and image blocks; route tool output through tool-result messages","Add a schema/arktype check on messages at the boundary where they enter your app","Test transcript round-trips (save/load) so block discriminators don't drift"],"tags":["bedrock","validation","content-blocks"],"backgroundTag":"unsupported-content-block-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}