{"record":{"id":"8ea4f2a29b0b8d5f","repo":"microsoft/autogen","slug":"expected-text-content-in-the-last-message-last-m","errorCode":null,"errorMessage":"Expected text content in the last message: {last_message.content}","messagePattern":"Expected text content in the last message: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_assistant_agent.py","lineNumber":515,"sourceCode":"        # Get messages after run completion\n        assistant_messages: AsyncCursorPage[Message] = await cancellation_token.link_future(\n            asyncio.ensure_future(\n                self._client.beta.threads.messages.list(thread_id=self._thread_id, order=\"desc\", limit=1)  # type: ignore[reportDeprecated]\n            )\n        )\n\n        if not assistant_messages.data:\n            raise ValueError(\"No messages received from assistant\")\n\n        # Get the last message's content\n        last_message = assistant_messages.data[0]\n        if not last_message.content:\n            raise ValueError(f\"No content in the last message: {last_message}\")\n\n        # Extract text content\n        text_content = [content for content in last_message.content if content.type == \"text\"]\n        if not text_content:\n            raise ValueError(f\"Expected text content in the last message: {last_message.content}\")\n\n        # Return the assistant's response as a Response with inner messages\n        chat_message = TextMessage(source=self.name, content=text_content[0].text.value)\n        yield Response(chat_message=chat_message, inner_messages=inner_messages)\n\n    async def handle_incoming_message(self, message: BaseChatMessage, cancellation_token: CancellationToken) -> None:\n        \"\"\"Handle regular text messages by adding them to the thread.\"\"\"\n        content: str | List[MessageContentPartParam] | None = None\n        llm_message = message.to_model_message()\n        if isinstance(llm_message.content, str):\n            content = llm_message.content\n        else:\n            content = []\n            for c in llm_message.content:\n                if isinstance(c, str):\n                    content.append(TextContentBlockParam(text=c, type=\"text\"))\n                elif isinstance(c, Image):\n                    content.append(ImageURLContentBlockParam(image_url=ImageURLParam(url=c.data_uri), type=\"image_url\"))","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_assistant_agent.py#L497-L533","documentation":"The last assistant message HAS content parts, but none of type 'text' — e.g. only image_file parts (image generation) or an unexpected part type. on_messages_stream hard-codes extraction of text content to build its TextMessage, so non-text-only replies cannot be returned and raise ValueError showing the actual content.","triggerScenarios":"An assistant configured for image generation returning only an image_file content part; assistants emitting annotations-only or other non-text parts as the final message.","commonSituations":"Using the Assistants API for image output while expecting text; instructions that let the model answer with a file/image and no prose.","solutions":["Instruct the assistant to always include a textual answer in its final message.","If you need image/file outputs, consume the thread messages yourself via agent.messages.list(...) instead of relying on on_messages' TextMessage extraction.","Inspect the printed last_message.content in the error to see which part types were returned, then adapt consumption accordingly."],"exampleFix":"# before\ninstructions = \"Generate an image of a cat.\"\n\n# after\ninstructions = (\n    \"Generate an image of a cat, then always describe the image \"\n    \"in a text reply to the user.\"\n)","handlingStrategy":"validation","validationCode":"page = await agent.messages.list(thread_id=agent._thread_id, order=\"desc\", limit=1)\nhas_text = page.data and any(\n    getattr(part, \"type\", None) == \"text\" for part in (page.data[0].content or [])\n)\nif not has_text:\n    # request a textual follow-up instead of calling on_messages","typeGuard":null,"tryCatchPattern":"try:\n    resp = await agent.on_messages(msgs, ct)\nexcept ValueError as e:\n    if \"Expected text content\" in str(e):\n        page = await agent.messages.list(thread_id=agent._thread_id, order=\"desc\", limit=1)\n        # consume image/file parts from page yourself\n    else:\n        raise","preventionTips":["Add 'always reply with text' to instructions for image/file-producing assistants.","For multimodal outputs, read thread messages directly rather than on_messages.","Check content part types before assuming text."],"tags":["openai","assistants-api","messages","multimodal"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}