draw_mermaid_png(): visual graph export
Why this matters
You need to understand your graph's structure: especially in production debugging, code reviews, and documentation. A visual export catches routing bugs and missing edges that text inspection misses. It's also the fastest way to explain your agentic architecture to non-engineers.
Explanation
What it is: draw_mermaid_png() is a method on compiled LangGraph graphs that exports the graph structure as a PNG image file. It introspects your nodes, edges, and control flow, then generates a Mermaid diagram and renders it.
How it works mechanically: When you call graph.compile().draw_mermaid_png(), LangGraph walks your StateGraph's topology: collecting node names, edge destinations, and conditional branches. It converts this to Mermaid flowchart syntax, then uses a Mermaid renderer (via an external service or local renderer) to produce a PNG. The method writes the file to disk at the path you specify.
When to use it: Call this after you've finalized your graph structure but before deployment. Use it in unit tests to assert graph topology hasn't changed, in documentation generators to auto-create architecture diagrams, and in debugging sessions when edge routing feels wrong.
Analogy
It's like exporting a circuit diagram from CAD software: the diagram doesn't run anything, but it shows you exactly how components connect so you can verify correctness before manufacturing.
Code
import os
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
message: str
processed: bool
def node_a(state: State) -> State:
return {**state, "message": f"Node A: {state['message']}"}
def node_b(state: State) -> State:
return {**state, "message": f"Node B: {state['message']}"}
def router(state: State) -> str:
if len(state["message"]) > 10:
return "node_b"
return "end"
def node_c(state: State) -> State:
return {**state, "processed": True}
graph = StateGraph(State)
graph.add_node("node_a", node_a)
graph.add_node("node_b", node_b)
graph.add_node("node_c", node_c)
graph.add_edge(START, "node_a")
graph.add_conditional_edges("node_a", router, {"node_b": "node_b", "end": END})
graph.add_edge("node_b", "node_c")
graph.add_edge("node_c", END)
compiled = graph.compile()
compiled.draw_mermaid_png(output_file_path="graph.png")
print("Graph exported to graph.png") Graph exported to graph.png
What just happened?
The code built a 4-node graph with conditional routing from node_a, then compiled it. The <code>draw_mermaid_png()</code> call introspected the graph topology, generated Mermaid flowchart syntax representing START → node_a → (conditional) → node_b → node_c → END, rendered it to PNG via the Mermaid renderer, and saved the file as <code>graph.png</code> in the current working directory.
Common gotcha
Developers often assume draw_mermaid_png() works on the StateGraph object itself. It doesn't: you must call it on graph.compile(), the compiled instance. Calling it on the uncompleted graph will raise AttributeError. Also, conditional edges only show their branch labels in the diagram if the router function returns exactly those keys: typos in return values create confusing diagrams.
Error recovery
FileNotFoundErrorAttributeError: 'StateGraph' object has no attribute 'draw_mermaid_png'ModuleNotFoundError: No module named 'PIL'Experienced dev note
Save the PNG in your Git repo alongside your graph definition code, then regenerate it in CI/CD on every commit. This auto-documents your graph changes in pull requests: reviewers see exactly what control flow changed without reading the Python. Add a pre-commit hook: compiled.draw_mermaid_png(output_file_path='docs/graph.png') as part of your test suite, so a forgotten graph update fails the build.
Check your understanding
You have a conditional edge from node_x that can route to either 'success_node' or 'retry_node' based on a router function. When you export the PNG, you see the edge labels show 'success_node' and 'retry_node' on the diagram. Your router function's return statement is: return 'failed' if error else 'ok'. What will the PNG actually show as edge labels, and why?
Show answer hint
A correct answer recognizes that the PNG will show the edge dictionary keys (the conditional_edges target dict), not the router's return values: so you'll see whatever keys you passed to <code>add_conditional_edges()</code>. If the router returns 'failed' but your edge dict has {'error': 'retry_node'}, there's a mismatch and the diagram won't reflect the actual routing.
draw_mermaid() and returned a string. In 0.2.0+, it's draw_mermaid_png() and writes directly to disk. If you need the Mermaid string without writing a file, call compiled.get_graph().draw_mermaid() instead.