microsoft/autogen · error · ValueError

The last message is not a BaseChatMessage.

Error message

The last message is not a BaseChatMessage.

What it means

TaskRunnerTool.return_value_as_string raises ValueError when created with return_value_as_last_message=True but the wrapped task's TaskResult has no messages or its last message is not a BaseChatMessage. The tool promises to return the final chat message as its string output, so a missing/invalid last message breaks that contract.

Source

Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/tools/_task_runner_tool.py:59

    async def run(self, args: TaskRunnerToolArgs, cancellation_token: CancellationToken) -> TaskResult:
        """Run the task and return the result."""
        return await self._task_runner.run(task=args.task, cancellation_token=cancellation_token)

    async def run_stream(
        self, args: TaskRunnerToolArgs, cancellation_token: CancellationToken
    ) -> AsyncGenerator[BaseAgentEvent | BaseChatMessage | TaskResult, None]:
        """Run the task and yield events or messages as they are produced, the final :class:`TaskResult`
        will be yielded at the end."""
        async for event in self._task_runner.run_stream(task=args.task, cancellation_token=cancellation_token):
            yield event

    def return_value_as_string(self, value: TaskResult) -> str:
        """Convert the task result to a string."""
        if self._return_value_as_last_message:
            if value.messages and isinstance(value.messages[-1], BaseChatMessage):
                return value.messages[-1].to_model_text()
            raise ValueError("The last message is not a BaseChatMessage.")
        parts: List[str] = []
        for message in value.messages:
            if isinstance(message, BaseChatMessage):
                if message.source == "user":
                    continue
                parts.append(f"{message.source}: {message.to_model_text()}")
        return "\n\n".join(parts)

    async def save_state_json(self) -> Mapping[str, Any]:
        return await self._task_runner.save_state()

    async def load_state_json(self, state: Mapping[str, Any]) -> None:
        await self._task_runner.load_state(state)

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Use return_value_as_last_message=False (default) so the tool joins all chat messages into a string instead of requiring a valid last message.
  2. Ensure the inner team always terminates after at least one BaseChatMessage (e.g. a MaxMessageTermination after a TextMessage rather than before any output).
  3. Pre-check the TaskResult before conversion: if result.messages and isinstance(result.messages[-1], BaseChatMessage).

Example fix

# before
tool = TaskRunnerTool(..., return_value_as_last_message=True)  # team may end without a chat message

# after
tool = TaskRunnerTool(..., return_value_as_last_message=False)  # falls back to joining all messages
Defensive patterns

Strategy: fallback

Validate before calling

result = await inner.run(task=task)
if not result.messages or not isinstance(result.messages[-1], BaseChatMessage):
    raise ValueError("Inner task produced no final chat message; check team termination config")

Type guard

from autogen_agentchat.messages import BaseChatMessage

def has_final_chat_message(result: TaskResult) -> bool:
    return bool(result.messages) and isinstance(result.messages[-1], BaseChatMessage)

Try / catch

try:
    return tool.return_value_as_string(result)
except ValueError:
    return "(task produced no final message)"  # or fall back to joining available messages

Prevention

When it happens

Trigger: Constructing TaskRunnerTool(description=..., args_type=..., return_value_as_last_message=True) around a team/agent whose run ends with zero messages or whose last item is an event (BaseAgentEvent) rather than a BaseChatMessage; running a task that terminates before any agent produces output.

Common situations: Wrapping a team that ends on an error/termination event without a final chat message; using the tool with return_value_as_last_message=True when the inner task yields only tool-call events.

Related errors


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