microsoft/autogen · error · ValueError

No content in the last message: {last_message}

Error message

No content in the last message: {last_message}

What it means

The last message fetched from the assistant thread exists (assistant_messages.data is non-empty) but its content field is empty/None, so the agent cannot build a TextMessage. Content is normally a list of content-part objects (text, image_file, ...); an empty list means the assistant produced a message with no body.

Source

Thrown at python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_assistant_agent.py:510

            if run.status == "completed":
                break

            await asyncio.sleep(0.5)

        # Get messages after run completion
        assistant_messages: AsyncCursorPage[Message] = await cancellation_token.link_future(
            asyncio.ensure_future(
                self._client.beta.threads.messages.list(thread_id=self._thread_id, order="desc", limit=1)  # type: ignore[reportDeprecated]
            )
        )

        if not assistant_messages.data:
            raise ValueError("No messages received from assistant")

        # Get the last message's content
        last_message = assistant_messages.data[0]
        if not last_message.content:
            raise ValueError(f"No content in the last message: {last_message}")

        # Extract text content
        text_content = [content for content in last_message.content if content.type == "text"]
        if not text_content:
            raise ValueError(f"Expected text content in the last message: {last_message.content}")

        # Return the assistant's response as a Response with inner messages
        chat_message = TextMessage(source=self.name, content=text_content[0].text.value)
        yield Response(chat_message=chat_message, inner_messages=inner_messages)

    async def handle_incoming_message(self, message: BaseChatMessage, cancellation_token: CancellationToken) -> None:
        """Handle regular text messages by adding them to the thread."""
        content: str | List[MessageContentPartParam] | None = None
        llm_message = message.to_model_message()
        if isinstance(llm_message.content, str):
            content = llm_message.content
        else:
            content = []

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Retry the turn — ask the model to produce a textual answer; add an instruction like 'Always finish with a short text summary.'
  2. Inspect the thread manually: client.beta.threads.messages.list(thread_id=..., limit=5) to see what content parts the last messages actually carry.
  3. Upgrade autogen-ext; handling of non-text final messages in on_messages_stream has been improved in later releases.

Example fix

# before
instructions = "You are a helpful assistant."

# after
instructions = (
    "You are a helpful assistant. "
    "Always conclude each turn with a plain-text answer to the user."
)
Defensive patterns

Strategy: retry

Try / catch

try:
    resp = await agent.on_messages(msgs, ct)
except ValueError as e:
    if "No content in the last message" in str(e):
        follow_up = [TextMessage(source="user", content="Please answer in plain text.")]
        resp = await agent.on_messages(msgs + follow_up, ct)
    else:
        raise

Prevention

When it happens

Trigger: Assistant emits only an image_file content part that is later stripped, or a run ends after tool calls with an empty final message; edge cases in the Assistants API where the newest message is a placeholder with no content parts.

Common situations: Runs that finish via tool-call paths with no final textual answer; content filtering removing output; fetching limit=1 grabbing a non-final message when ordering/limitation behaves unexpectedly.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/122b6b775f9dfe00. Report an issue: GitHub.