> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Defining the Graph State Schema

> Explains designing and typing a shared graph state schema for LangGraph workflows to improve reliability, observability, and collaboration using Python TypedDicts and best practices.

In LangGraph, every node exchanges information through a shared structure called the graph state. A clear schema for that state is essential: it defines what the system knows, how knowledge evolves, and what each node should expect to read or write. Without a schema, nodes can assume fields that are never provided or accidentally overwrite critical data—leading to brittle, hard-to-debug graphs.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/A72Yow2kiLd9Lv77/images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-schema-flowchart-reliability.jpg?fit=max&auto=format&n=A72Yow2kiLd9Lv77&q=85&s=f8b615cc35fb54f3a2850238db303724" alt="The image is a flowchart illustrating the importance of defining a graph state schema, highlighting reliability and explainability. It shows &#x22;Node A&#x22; interacting with a graph state that lacks a schema, which can lead to issues like unprovided summaries and overwritten values." width="1920" height="1080" data-path="images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-schema-flowchart-reliability.jpg" />
</Frame>

Why a schema matters

* Improves reliability by preventing missing keys and type mismatches.
* Increases explainability by making the data surface explicit.
* Enables safer collaboration between nodes and teams.
* Makes observability and debugging easier because tools can inspect well-known fields.

The graph state as a single source of truth
LangGraph’s graph state is a single Python dictionary passed from node to node. Each node receives the state, may read and update it, and then passes the updated state forward. Think of it as a shared document that all nodes can edit: it provides memory, tracks tool usage, and stores intermediate outputs needed to build cross-node context.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/A72Yow2kiLd9Lv77/images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-diagram-nodes-python-dictionary.jpg?fit=max&auto=format&n=A72Yow2kiLd9Lv77&q=85&s=7f6a6ff98984502f7305294352e76287" alt="The image explains the concept of &#x22;Graph State&#x22; with a diagram showing nodes A, B, and C, state as a Python dictionary, and the idea of a &#x22;single source of truth&#x22;. It emphasizes that each step reads, updates, and passes along the state." width="1920" height="1080" data-path="images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-diagram-nodes-python-dictionary.jpg" />
</Frame>

Typical fields you’ll find in LangGraph workflows
Common state fields include the user input, predicted intent, results returned by tools, chat history, and the final response. You’ll often also store flags, metadata, timestamps, or loop counters—anything that helps guide the execution flow.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/A72Yow2kiLd9Lv77/images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-data-flow-input-intent-results-response.jpg?fit=max&auto=format&n=A72Yow2kiLd9Lv77&q=85&s=1e53fbe26b4382f504e2d5d3278eee5f" alt="The image outlines the common data in a graph state, consisting of four stages: Input (user's original message), Intent (predicted intent of message), Results (output from tools), and Response (final generated answer)." width="1920" height="1080" data-path="images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/graph-state-data-flow-input-intent-results-response.jpg" />
</Frame>

Use TypedDict to declare a schema
To make the state explicit and machine-checkable, LangGraph recommends declaring the schema using Python’s `TypedDict` (or `typing_extensions.TypedDict` for older versions). This provides a contract between nodes and allows static type checkers like [mypy](https://mypy-lang.org/) to catch inconsistencies early.

Example TypedDict schema:

```python theme={null}
from typing import List, Dict, Any
from typing_extensions import TypedDict, NotRequired
from langchain.schema import BaseMessage

class GraphState(TypedDict, total=False):
    # required at the start of a run
    input: str

    # optional fields populated as the graph executes
    intent: NotRequired[str]
    chat_history: NotRequired[List[BaseMessage]]
    tool_results: NotRequired[Dict[str, Any]]
    final_response: NotRequired[str]
    loop_count: NotRequired[int]
```

Notes about this pattern

* Using `total=False` makes keys optional by default. Alternatively, use `total=True` and mark selective keys with `NotRequired` (see [PEP 655](https://peps.python.org/pep-0655/))—choose the pattern that best fits your project.
* `chat_history` is typed as `List[BaseMessage]` (LangChain message types) to preserve conversational context.
* `tool_results` is a flexible `Dict[str, Any]` because outputs differ between integrations.

Why typing helps

* Static typing and `TypedDict` let tools verify nodes read/write the correct keys and types.
* Observability systems can record which fields exist at each node execution to simplify debugging and audits.
* A typed schema clarifies expectations across teams and over time, improving maintainability.

Field-by-field breakdown

| Field            | Type                | Purpose                                                                                                               |
| ---------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `input`          | `str`               | The user’s original message or command; present at the start of a run.                                                |
| `intent`         | `str`               | Typically set by an intent-classification node to indicate user intent (question, command, tool request, etc.).       |
| `chat_history`   | `List[BaseMessage]` | Stores conversation messages (e.g., `HumanMessage`, `AIMessage`, `SystemMessage`) needed for memory or summarization. |
| `tool_results`   | `Dict[str, Any]`    | Outputs from external tools; structure can be dynamic, e.g. `{"openweather": {"temp_f": 55, "condition": "Rain"}}`.   |
| `final_response` | `str`               | The composed answer produced by the final node.                                                                       |
| `loop_count`     | `int`               | Tracks iteration counts in cyclical graphs to help enforce safe exit conditions.                                      |

Example usage pattern

```python theme={null}
state: GraphState = {"input": "What's the weather in Seattle tomorrow?"}

# After intent classification
state["intent"] = "weather_query"

# After invoking a weather tool
state["tool_results"] = {"openweather": {"temp_f": 55, "condition": "Rain"}}

# Compose final response and increment loop counter
state["final_response"] = "Expect rain tomorrow with a high near 55°F."
state["loop_count"] = state.get("loop_count", 0) + 1
```

<Callout icon="lightbulb" color="#1CB2FE">
  Design your schema to be permissive early (optional fields) and grow it intentionally as new nodes or tools are added. This balances development speed with safety and observability.
</Callout>

Practical considerations and best practices

* Define the schema early in the project to reduce ambiguity and accidental overwrites.
* Treat the schema as a living contract: extend it when you add tools, nodes, or new observability needs.
* Use loop counters and explicit success/error flags to make iterative or cyclical graphs safe and auditable.
* Prefer clear, well-documented keys over deep opaque blobs—this improves explainability and cross-team collaboration.
* Leverage static analysis (`mypy`) and runtime checks in critical nodes to catch unexpected types or missing keys early.

When to update the schema

* When you add new nodes or tool integrations that need new fields.
* When data flows change, require additional metadata, or new observability signals.
* As part of routine maintenance: keep the schema aligned with runtime telemetry and logs.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/A72Yow2kiLd9Lv77/images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/schema-update-situations-nodes-data-flow.jpg?fit=max&auto=format&n=A72Yow2kiLd9Lv77&q=85&s=763be372faa3c8fea5014e31cee06854" alt="The image lists three situations for updating a schema: when adding new nodes or tools, when data flow changes or expands, and to keep it a living structure." width="1920" height="1080" data-path="images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/schema-update-situations-nodes-data-flow.jpg" />
</Frame>

Conclusion
A clear graph state schema is the foundation of robust LangGraph workflows. It ensures consistency, transparency, and safety while speeding up debugging and cross-team collaboration. Define the schema early, document it, and evolve it deliberately as your system grows—just like Robbie refining his delivery journal as routes become more complex.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/A72Yow2kiLd9Lv77/images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/schema-design-takeaways-layout.jpg?fit=max&auto=format&n=A72Yow2kiLd9Lv77&q=85&s=6f29ec42e0eac91f315719767b9f471f" alt="The image contains a layout with takeaways on schema design, emphasizing foundations, consistency, early definition, and evolution as complexity grows. It features numbered points and a dark sidebar labeled &#x22;Takeaways.&#x22;" width="1920" height="1080" data-path="images/LangGraph/State-Management-and-Iterative-Loops/Defining-the-Graph-State-Schema/schema-design-takeaways-layout.jpg" />
</Frame>

Further reading and references

* Python `TypedDict` docs: [https://docs.python.org/3/library/typing.html#typing.TypedDict](https://docs.python.org/3/library/typing.html#typing.TypedDict)
* PEP 655 (optional keys): [https://peps.python.org/pep-0655/](https://peps.python.org/pep-0655/)
* mypy static type checker: [https://mypy-lang.org/](https://mypy-lang.org/)
* LangChain message types: [https://python.langchain.com/en/latest/](https://python.langchain.com/en/latest/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/langgraph/module/7a80b285-b366-4c4d-95d0-bce0c24aaf58/lesson/da3e22f3-9144-425a-bc83-4715c077857c" />
</CardGroup>
