Code Intermediate medium · 6 min

LangSmith tracing for graphs

What you will learn
Connect your LangGraph to LangSmith to visualize execution flow, debug decisions, and trace token usage across all nodes.

Why this matters

Production graphs fail silently in ways batch jobs don't. You need visibility into which node decided what, when it called the LLM, and why a decision took a different path. LangSmith tracing gives you that without rewriting your graph.

Skip if: Skip LangSmith tracing for local development on toy graphs with fewer than 3 nodes, or when you're only testing deterministic logic paths. Do not use tracing as a substitute for unit tests: trace to debug, not to validate.

Explanation

What it is: LangSmith is LangChain's observability platform. When you connect your LangGraph to LangSmith, every node execution, LLM call, and tool invocation is recorded and visualized in the LangSmith web UI. You see the exact input/output of each step, token counts, latencies, and branching decisions.

How it works mechanically: You set environment variables pointing to your LangSmith project, then pass langsmith_api_key when compiling your graph (or let it auto-detect from LANGCHAIN_API_KEY). The tracing_v2_enabled environment variable activates tracing. Every invoke() or stream() call automatically logs to LangSmith. The platform records parent-child relationships, so you see the exact call tree: node A called an LLM, which returned data to node B, which branched to either C or D.

When to use it: Enable tracing in dev and staging always. In production, enable it for a percentage of requests (sampling) to avoid performance overhead and cost. Use it heavily when debugging why a graph chose an unexpected path, or when iterating on prompt quality.

Analogy

Think of LangSmith like browser DevTools for your graph. Just as you open DevTools to see network requests, timings, and the call stack, you open LangSmith to see every LLM call, every conditional branch, and how long each node ran.

Code

Illustrative only - not runnable without a valid API key
python
import os
from anthropic import Anthropic
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
from operator import add

os.environ['LANGCHAIN_TRACING_V2'] = 'true'
os.environ['LANGCHAIN_API_KEY'] = 'ls__your_actual_key_here'
os.environ['LANGCHAIN_PROJECT'] = 'langgraph-demo'

class State(TypedDict):
    question: str
    reasoning: Annotated[list, add]
    answer: str

client = Anthropic()

def analyze_node(state: State) -> State:
    response = client.messages.create(
        model='claude-3-5-sonnet-20241022',
        max_tokens=150,
        messages=[{
            'role': 'user',
            'content': f'Analyze this question briefly: {state["question"]}'
        }]
    )
    reasoning = response.content[0].text
    return {'reasoning': [reasoning], 'answer': ''}

def answer_node(state: State) -> State:
    response = client.messages.create(
        model='claude-3-5-sonnet-20241022',
        max_tokens=200,
        messages=[{
            'role': 'user',
            'content': f'Question: {state["question"]}\nAnalysis: {state["reasoning"][0]}\n\nProvide a clear answer.'
        }]
    )
    answer = response.content[0].text
    return {'answer': answer, 'reasoning': []}

graph = StateGraph(State)
graph.add_node('analyze', analyze_node)
graph.add_node('answer', answer_node)
graph.add_edge(START, 'analyze')
graph.add_edge('analyze', 'answer')
graph.add_edge('answer', END)

compiled_graph = graph.compile()

result = compiled_graph.invoke({
    'question': 'What is the capital of France?',
    'reasoning': [],
    'answer': ''
})

print(f"Question: {result['question']}")
print(f"Answer: {result['answer']}")
Output
Question: What is the capital of France?
Answer: The capital of France is Paris. Paris has been France's capital since the 12th century and is located in the north-central part of the country along the Seine River. It's not only the political center of government, but also the cultural, economic, and educational heart of France, famous for landmarks like the Eiffel Tower, Notre-Dame Cathedral, and the Louvre Museum.

What just happened?

The graph was compiled with tracing enabled via environment variables. When <code>invoke()</code> ran, it executed the 'analyze' node (which called Claude), then the 'answer' node (which called Claude again with the analysis as context). Each LLM call and node transition was automatically sent to LangSmith. If you were logged into LangSmith with a valid API key and project name, you would see a trace in the UI showing the call tree: START → analyze (with its LLM call details) → answer (with its LLM call details) → END, complete with token counts and latencies.

Common gotcha

The biggest mistake is setting LANGCHAIN_TRACING_V2=true but forgetting to set LANGCHAIN_API_KEY or setting a fake key. The graph will still run (no error), but traces silently fail to upload. You'll think tracing is working, check LangSmith, see nothing, and waste 20 minutes. Always verify your first trace appears in the UI before debugging anything else.

Error recovery

AuthenticationError during graph execution
Your LANGCHAIN_API_KEY is invalid or expired. Go to LangSmith settings, generate a new API key, and update the environment variable. Restart your process.
Traces not appearing in LangSmith UI
Check that LANGCHAIN_PROJECT is set to an exact project name that exists in your LangSmith workspace. If the project doesn't exist, LangSmith will create it, but check the project dropdown to confirm it was created. If LANGCHAIN_TRACING_V2 is set to 'false' or not set at all, no traces are sent: verify it is the string 'true'.
LLM calls tracing but nodes not showing up
You are using node names that don't match the graph's node structure, or the graph wasn't compiled after adding nodes. Ensure <code>graph.compile()</code> is called after all edges are added.

Experienced dev note

In production, you do NOT want to trace every request: it adds latency and costs money. Instead, set os.environ['LANGCHAIN_TRACING_SAMPLE_RATE'] = '0.1' to trace 10% of requests randomly. This gives you enough signal to catch bugs without the overhead. Pair this with LANGCHAIN_TAGS to mark traces by feature or user segment, so you can filter in the UI and correlate traces with actual user reports.

Check your understanding

Your graph has 5 nodes and traces are enabled, but you notice that node 3 never appears in the LangSmith UI trace, even though you know it executed (you added debug prints to it and saw the output). What are two possible reasons this could happen, and how would you confirm which one it is?

Show answer hint

One reason: the node doesn't call any LLM or tool: tracing only shows operations that interact with LangChain integrations (LLMs, tools, retrievers). Pure Python logic is invisible to tracing. Second reason: the node exists but was added to the graph after calling <code>graph.compile()</code>, or there's a code path that skips that node via conditional edges. Check by adding a logging statement or by inspecting the actual trace JSON in LangSmith's raw API response.

VERSION LangSmith tracing with LANGCHAIN_TRACING_V2 is stable in langgraph 0.2.x. The API changed in langgraph 0.1.x where tracing required passing a callback handler directly. Use environment variables in 0.2.x: it's cleaner and requires no code changes.
NEXT

Next, learn how to add <strong>human-in-the-loop checkpoints</strong> to your graph so you can pause execution, review decisions in LangSmith, and decide whether to continue or modify state before proceeding.

Community Notes

No notes yetBe the first to share a version-specific fix or tip.