How to Trace LangGraph Agents in 2026 - Node-Level Spans, Loops and Failure Debugging
A practical guide to tracing LangGraph state machines - get one span per node, see the state at every edge, catch runaway loops, and pin down which node actually failed. With the tools that fit and their honest trade-offs.
Published:
LangGraph is a state machine. That single fact is why tracing it is different from tracing a linear chain, and why a flat request log is almost useless for debugging one. Your agent is a graph of nodes connected by edges, some of them conditional, some of them loops - and when it misbehaves, the question is never just “what did the model say.” It is “which node ran, in what order, on what state, and why did the graph route the way it did.” A good trace answers all of that. Here is how to get one.
Why LangGraph needs node-level tracing specifically
A LangChain chain runs top to bottom. A LangGraph agent can branch, revisit nodes, and loop until a condition flips - so the interesting bugs are structural. The agent picked the wrong branch. It looped nine times before hitting the recursion limit. A node mutated state in a way that broke the node three hops later. None of those show up in a flat list of model calls - you have to see the graph execution as a graph.
That means the trace you want has one span per node, nested under a single root span for the whole run, in execution order. When node B runs twice, you see two B spans. When the conditional edge routes to end instead of retry, you see the decision and the state that drove it. That structure is the whole point, and it is what separates real tracing from logging.
Step 1: Instrument the compiled graph, not each node
The mistake here is wrapping every node function by hand. You do not need to. LangGraph emits standard LangChain callback events, so you attach one handler to the whole graph and get every node for free.
The cleanest path with the open-source default looks like this - illustrative, verify against current docs:
from langfuse.langchain import CallbackHandler
handler = CallbackHandler()
# graph is your compiled StateGraph
result = graph.invoke(
{"messages": [{"role": "user", "content": "Book me a flight to Berlin"}]},
config={"callbacks": [handler]},
)
One handler, passed in config, traces the entire graph run. Langfuse captures each node as a span, records the model calls inside them, and nests the whole thing under one trace - and because it is a LangChain callback, it does not care that the graph loops or branches. Langfuse is MIT-licensed and free to self-host, which is why it is the usual starting point. The catch is operational, not functional - the v3 self-host is a four-service stack, so if you would rather not run ClickHouse and Redis, the free cloud tier sidesteps it. This is the same callback pattern covered in the Langfuse LangChain integration guide, and it carries straight over to LangGraph.
Step 2: Make sure state is visible at every edge
Tracing the nodes is half the job. The other half is seeing the state object as it flows between them, because in LangGraph the state is the thing that drives routing. If a conditional edge sends the agent down the wrong path, the reason is in the state that the router read.
A good trace records each node’s input state and output state, so you can scrub across the run and watch state mutate. When you are debugging “why did it call the search tool again,” you open the span before the routing decision and read the exact state values the edge condition saw. The state diff between nodes is where most LangGraph bugs actually live - a field that never got set, a list that grew unbounded, a flag that flipped when it should not have. Insist on a tool that captures inputs and outputs per node, not just the final answer.
Step 3: Catch loops before they cost you
LangGraph loops are a feature and a footgun. A retry loop that never satisfies its exit condition will spin until it hits the recursion limit, burning tokens on every pass. Set a recursion limit so the graph raises instead of running forever, then use the trace to find the misfiring edge:
result = graph.invoke(
state,
config={"callbacks": [handler], "recursion_limit": 15},
)
In the trace, a loop is unmistakable - the same node span repeats with near-identical state, and the conditional edge keeps routing back instead of to the end. You read the router’s view on each pass and find the condition that never flips. Without the node-level trace you would only see a token bill and a timeout. With it, the loop is a shape you can point at.
Step 4: Pin the failure to a node
When a run fails, you want the blame localized to one node, not the whole agent. A node-structured trace does this by construction - the span that errored is the node that failed, and its input state tells you what it choked on. Was it a tool call with bad arguments? A model call that returned malformed JSON the next node could not parse? The failing span holds the answer, and the parent spans show the path that led there. This is the same debugging discipline as evaluating AI agents - you cannot fix a trajectory you cannot see.
Which tool for LangGraph tracing?
- Open-source, self-hostable, framework-agnostic - Langfuse as a LangChain callback. MIT-licensed, free to self-host, $29/mo cloud if you skip the ops. The default recommendation for most teams.
- Regression testing on top of tracing - Braintrust traces LangGraph through its LangChain and OpenTelemetry integrations and adds CI/CD quality gates that block a merge on a statistically significant regression. Watch its processed-data billing meter, which verbose agents run up fastest.
- All-in on the LangChain stack - LangSmith has the tightest LangGraph integration since both are from the same team, but it is expensive at volume and self-host is Enterprise-only.
For most teams, the answer is a LangChain callback handler pointed at Langfuse - one line in your config, node-level spans, and free to run. For the wider field see the best LLM tracing tools roundup and the best LLM observability for LangChain guide. Code shape here follows each tool’s standard integration pattern - verify against current docs, since this category ships breaking changes monthly.
Frequently Asked Questions
How do you trace a LangGraph agent?
Attach a callback handler or OpenTelemetry instrumentation to the compiled graph, then run it as normal. A good handler emits one span per node so the trace mirrors your graph - you see which node ran, what state came in, what it returned, and how the graph moved along each edge. Langfuse plugs in as a LangChain callback and works with LangGraph out of the box; Braintrust and LangSmith do the same through their integrations. The key is that the trace should be node-structured, not one flat blob, so you can debug at the level you actually built the agent.
Why is my LangGraph agent stuck in a loop?
Almost always a conditional edge that never routes to the end node, or a node that keeps setting the same state that triggers the same branch. A trace makes this obvious - you see the same node fire over and over with near-identical state, and you can read the exact values the router saw on each pass. Add a recursion limit so the graph raises instead of spinning forever, then use the trace to find which edge condition is misfiring. Without a node-level trace you are guessing at a loop you cannot see.
Do I need LangSmith to trace LangGraph?
No. LangGraph and LangSmith are both from the LangChain team so the integration is tight, but LangGraph emits standard LangChain callback events, which means any tool that consumes those events can trace it. Langfuse is the open-source option - MIT-licensed, free to self-host, and it captures LangGraph runs as a callback handler. Braintrust works through OpenTelemetry and its LangChain integration. You are not locked to LangSmith just because you chose LangGraph.
What should a LangGraph trace show me?
Four things. The node structure - one span per node in the order they ran. The state at each step - what each node received and returned, so you can see how state mutated across the graph. The routing decisions - which conditional edge fired and why. And the model calls inside each node - prompt, response, tokens and latency. If your trace shows all four you can debug loops, wrong branches and bad tool calls without adding print statements. If it only shows a flat list of LLM calls, you have logging, not tracing.
Explore More
Tool Reviews
Related Articles
- The Best LangSmith Alternatives in 2026, Ranked by Why Teams Actually Leave
- The Best Prompt Management Tools in 2026, Ranked for Versioning and Team Workflow
- Braintrust vs LangSmith 2026 - Turnkey Evals vs LangChain Depth
- Langfuse vs LangSmith in 2026 - The Honest Head-to-Head
- Langfuse vs LangSmith vs Braintrust in 2026 - Pick by What You Actually Need
Free Newsletter
Get the LLM Evals Newsletter
Platform comparisons, pricing changes and eval technique deep-dives. No spam.
Related Articles
How to Detect Prompt Injection in 2026 - Guardrails, Eval Tests and Red-Teaming
A practical guide to detecting direct and indirect prompt injection - input and output guardrails at the gateway, eval tests that catch it in CI, and red-teaming that finds the attacks you did not think of. With the tools that fit and their trade-offs.
July 28, 2026
how-toHow to Evaluate AutoGen Agents in 2026 - Multi-Turn Runs, Loop Convergence and Termination
A practical guide to evaluating Microsoft AutoGen conversational multi-agent runs - score whether the conversation converged, terminated cleanly, stayed on task, and produced a correct result, so you can tell a productive loop from an infinite one.
July 28, 2026
how-toHow to Evaluate CrewAI Agents in 2026 - Task Completion, Handoffs and Per-Agent Scoring
A practical guide to evaluating CrewAI multi-agent crews - score the crew's final output, each agent's task completion, the handoffs between them, and the tool calls, so you know which agent to fix. With the tools that fit and their trade-offs.
July 28, 2026
Langfuse Review
Braintrust Review
LangSmith Review