compile(): what it does and why it's required
Why this matters
You cannot invoke or execute a graph without calling <code>compile()</code> first: it's the bridge between your graph structure and actual execution. Understanding what it does prevents confusion when your code doesn't run.
Explanation
What it is: compile() is a method on a StateGraph object that finalizes your graph definition and returns a runnable, executable object. You call it once per graph after all nodes and edges are defined. How it works mechanically: compile() validates your graph structure (checks that all referenced nodes exist, edges are properly connected), optimizes internal routing, and wraps everything into a CompiledGraph object. This compiled object has methods like invoke() and stream() that actually execute the workflow. When to use it: Always call compile() immediately after your last add_edge() call, before you try to run anything. It's a one-time operation per graph instance.
Analogy
Think of <code>compile()</code> like pressing "Export" on a blueprint before you can use the machine. The blueprint (your graph definition) is just a document until you export it into something that actually runs (the compiled executable).
Code
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class State(TypedDict):
count: int
def increment_node(state: State) -> State:
return {"count": state["count"] + 1}
def double_node(state: State) -> State:
return {"count": state["count"] * 2}
graph = StateGraph(State)
graph.add_node("increment", increment_node)
graph.add_node("double", double_node)
graph.add_edge(START, "increment")
graph.add_edge("increment", "double")
graph.add_edge("double", END)
compiled_graph = graph.compile()
result = compiled_graph.invoke({"count": 5})
print(result) {"count": 12} What just happened?
We defined a state schema and two nodes (increment and double), connected them with edges, then called <code>compile()</code> which returned a <code>CompiledGraph</code> object. We then invoked that compiled graph with initial state <code>{"count": 5}</code>. The graph executed both nodes in sequence: 5 → 6 (increment) → 12 (double), and returned the final state.
Common gotcha
Developers often call add_edge() and then immediately try to call invoke() on the StateGraph object itself, forgetting that invoke()` is a method on the *compiled* object, not the graph builder. You must save the return value of compile() and call invoke() on that.
Error recovery
AttributeError: 'StateGraph' object has no attribute 'invoke'ValueError: Node 'xyz' not foundAttributeError: 'StateGraph' object has no attribute 'stream'Experienced dev note
In older langgraph versions (< 0.2.0), MessageGraph was used instead of StateGraph, and the compile step was less explicit in documentation. Modern langgraph makes the boundary crystal clear: build the graph, compile it once, then invoke it as many times as you want. Reuse the compiled graph object across multiple invocations: don't recompile. This is both more intuitive and more performant.
Check your understanding
If you compile the same graph twice and store the result in two different variables, then invoke both, would they produce identical results for the same input? Why or why not?
Show answer hint
A correct answer recognizes that compile produces an independent executable snapshot, so two compiled objects from the same graph definition are separate instances. Invocations are isolated: one doesn't affect the other's state. The real insight is that compile *freezes* the graph structure at that moment, so changes to the original StateGraph after compile don't affect already-compiled copies.
langgraph.graph. In < 0.2.0, these were sometimes strings ('START', 'END'). Always use the imported constants in 0.2.x+.