# How to retireve the final AI message after .invoke?

**URL:** <https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078>\
**Category:** LangChain\
**Tags:** intro-to-langgraph, python-help\
**Created:** [November 4, 2025, 10:25am UTC](https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078 "2025-11-04T10:25:50Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Kelbiro](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/kelbiro/32/1507_2.png) [@Kelbiro](https://forum.langchain.com/u/Kelbiro)\
**Post date:** [November 4, 2025, 10:25am UTC](https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078/1 "2025-11-04T10:25:50Z")

</div>

Hello !

Im invoking my agent using .invoke and outputting the last message present in the messages list in agent state.

but for some reason sometimes its content is a type of dict[str, list] instead of plain string.

Why is that? And whats going wrong ?

---

<div class="post-metadata">

**Author:** ![pawel-twardziak](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/pawel-twardziak/32/960_2.png) [@pawel-twardziak](https://forum.langchain.com/u/pawel-twardziak)\
**Post date:** [November 4, 2025, 11:22am UTC](https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078/2 "2025-11-04T11:22:37Z")

</div>

hi @Kelbiro

Nothing is wrong. In LangChain/LangGraph, a message’s `content` can be either a plain string or a list of structured “content blocks” (dicts). When your model/tooling returns multimodal or tool-call data, `content` will be a list of typed blocks (e.g., `{"type": "text", "text": ...}`, `{"type": "tool_call", ...}`) rather than a single string. To consistently get just the human‑readable text, take the last assistant (`AIMessage`) and read its `.text` property, which safely extracts textual parts.

Why you’re seeing dicts

- **Message content is intentionally polymorphic.** The `content` field is defined as `str | list[str | dict]` in LangChain core. This lets messages carry multimodal data and tool calls in a standardized way.

- **Providers and tools add structured blocks.** Tool calls, reasoning blocks, images/files, etc. are represented as typed dicts inside the list of content blocks.

### How to reliably get the final text

**Filter for the last assistant (`AIMessage`) and use `.text`:**

```python
# result is the output of graph.invoke(...)
messages = result["messages"]
# Find the last assistant message (the graph may end on a ToolMessage otherwise)
last_ai = next(m for m in reversed(messages) if getattr(m, "type", None) == "ai")
# Safely extract human-readable text
final_text = last_ai.text

```

Notes

- If your graph finishes on a tool node, the last message may be a `ToolMessage`. In that case you still want the last `AIMessage` before it.

- If you need OpenAI-like formatting, you can ask LangGraph to format messages by using `add_messages(format="langchain-openai")` in your state reducer; this will keep tool calls and multimodal blocks standardized when you inspect them.

---

<div class="post-metadata">

**Author:** ![Kelbiro](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/kelbiro/32/1507_2.png) [@Kelbiro](https://forum.langchain.com/u/Kelbiro)\
**Post date:** [November 4, 2025, 11:36am UTC](https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078/3 "2025-11-04T11:36:07Z")

</div>

Oh ic !

thanks alot

---

<div class="post-metadata">

**Author:** ![Kelbiro](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/kelbiro/32/1507_2.png) [@Kelbiro](https://forum.langchain.com/u/Kelbiro)\
**Post date:** [November 4, 2025, 11:40am UTC](https://forum.langchain.com/t/how-to-retireve-the-final-ai-message-after-invoke/2078/4 "2025-11-04T11:40:54Z")

</div>

Btw why this doesnt happen in langgraph ?

And where its exactly mentioned ?
