LangGraphLangGraph 1.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
38 small wins to finish your pathNext lesson →

Runtime context: per-run data with context_schema

Runtime context is per-run data, such as a user id, an API client or a model choice, passed to nodes and tools through context_schema so it stays out of the graph's state.

Last updated: 27 Sep, 2026 · LangGraph 1.2

State is the data that flows and changes between nodes. Some things are fixed for a whole run, like who is asking or which database to use. Those do not belong in state; they belong in the run's context.

Declaring a context schema

Describe the run's fixed data as a dataclass and hand it to StateGraph as context_schema.

python
from dataclasses import dataclass
from langgraph.graph import StateGraph, START, END
from langgraph.runtime import Runtime

@dataclass
class Context:          # fixed for the whole run
    user: str

Reading context in a node

A node takes a second argument, runtime, and reads the values off runtime.context. The node never puts them in state.

python
def greet(state, runtime: Runtime[Context]):
    # read per-run data off runtime.context, not off state
    return {"greeting": f"Hello, {runtime.context.user}!"}

Passing context at run time

Build the graph on the context schema, then pass a context object to invoke with the context= argument.

python
class State(TypedDict):
    greeting: str

b = StateGraph(State, context_schema=Context)   # declare the context
b.add_node("greet", greet)
b.add_edge(START, "greet")
b.add_edge("greet", END)
python
print(graph.invoke({"greeting": ""}, context=Context(user="Ada"))["greeting"])

Greeting a named user end to end

The whole program in one file.

Example
from dataclasses import dataclass
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.runtime import Runtime

@dataclass
class Context:
    user: str

class State(TypedDict):
    greeting: str

def greet(state, runtime: Runtime[Context]):
    return {"greeting": f"Hello, {runtime.context.user}!"}

b = StateGraph(State, context_schema=Context)
b.add_node("greet", greet)
b.add_edge(START, "greet")
b.add_edge("greet", END)
graph = b.compile()

print(graph.invoke({"greeting": ""}, context=Context(user="Ada"))["greeting"])

What the run used

  • context_schema=Context told the graph what fixed data a run carries.
  • greet read runtime.context.user; the value never entered state.
  • invoke(..., context=Context(user="Ada")) supplied it for this one run.

State vs context

StateContext
Changes between nodesYesNo, fixed for the run
A node can write itYes, by returning keysNo, it is read-only
Saved by the checkpointerYesNo
Use forData the graph builds upA user id, a client, a model choice

When to use context

  • The caller's identity or tenant, so a tool acts for the right user.
  • An API client, database handle or model choice shared by every node.
  • Wiring a user id into the store's namespace so memory is per user.
Watch out. Context is not state. A node cannot change it, the checkpointer does not save it, and it is set fresh on every invoke. Put anything that must change or persist in state instead.
Try it yourself
  • Add a tier field to Context and greet gold users differently.
  • Read runtime.context inside a tool and act for that user.
  • Try to write the user into state and see why context is the better home for it.

This is what real progress feels like.