{"record":{"id":"1a61f9a73637d0a9","repo":"microsoft/autogen","slug":"the-last-message-is-not-a-basechatmessage","errorCode":null,"errorMessage":"The last message is not a BaseChatMessage.","messagePattern":"The last message is not a BaseChatMessage\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-agentchat/src/autogen_agentchat/tools/_task_runner_tool.py","lineNumber":59,"sourceCode":"\n    async def run(self, args: TaskRunnerToolArgs, cancellation_token: CancellationToken) -> TaskResult:\n        \"\"\"Run the task and return the result.\"\"\"\n        return await self._task_runner.run(task=args.task, cancellation_token=cancellation_token)\n\n    async def run_stream(\n        self, args: TaskRunnerToolArgs, cancellation_token: CancellationToken\n    ) -> AsyncGenerator[BaseAgentEvent | BaseChatMessage | TaskResult, None]:\n        \"\"\"Run the task and yield events or messages as they are produced, the final :class:`TaskResult`\n        will be yielded at the end.\"\"\"\n        async for event in self._task_runner.run_stream(task=args.task, cancellation_token=cancellation_token):\n            yield event\n\n    def return_value_as_string(self, value: TaskResult) -> str:\n        \"\"\"Convert the task result to a string.\"\"\"\n        if self._return_value_as_last_message:\n            if value.messages and isinstance(value.messages[-1], BaseChatMessage):\n                return value.messages[-1].to_model_text()\n            raise ValueError(\"The last message is not a BaseChatMessage.\")\n        parts: List[str] = []\n        for message in value.messages:\n            if isinstance(message, BaseChatMessage):\n                if message.source == \"user\":\n                    continue\n                parts.append(f\"{message.source}: {message.to_model_text()}\")\n        return \"\\n\\n\".join(parts)\n\n    async def save_state_json(self) -> Mapping[str, Any]:\n        return await self._task_runner.save_state()\n\n    async def load_state_json(self, state: Mapping[str, Any]) -> None:\n        await self._task_runner.load_state(state)\n","sourceCodeStart":41,"sourceCodeEnd":73,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-agentchat/src/autogen_agentchat/tools/_task_runner_tool.py#L41-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Ensure the inner team always terminates after at least one BaseChatMessage (e.g. a MaxMessageTermination after a TextMessage rather than before any output).","Pre-check the TaskResult before conversion: if result.messages and isinstance(result.messages[-1], BaseChatMessage)."],"exampleFix":"# before\ntool = TaskRunnerTool(..., return_value_as_last_message=True)  # team may end without a chat message\n\n# after\ntool = TaskRunnerTool(..., return_value_as_last_message=False)  # falls back to joining all messages","handlingStrategy":"fallback","validationCode":"result = await inner.run(task=task)\nif not result.messages or not isinstance(result.messages[-1], BaseChatMessage):\n    raise ValueError(\"Inner task produced no final chat message; check team termination config\")","typeGuard":"from autogen_agentchat.messages import BaseChatMessage\n\ndef has_final_chat_message(result: TaskResult) -> bool:\n    return bool(result.messages) and isinstance(result.messages[-1], BaseChatMessage)","tryCatchPattern":"try:\n    return tool.return_value_as_string(result)\nexcept ValueError:\n    return \"(task produced no final message)\"  # or fall back to joining available messages","preventionTips":["Prefer return_value_as_last_message=False unless the inner team reliably ends with a chat message.","Configure termination so at least one TextMessage is emitted before the team stops."],"tags":["autogen","task-runner-tool","tools","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}