Skip to main content
One adapter serves both. LangGraph runs on langchain-core’s callback manager, so instrumenting one instruments the other.

Install

For LangChain without LangGraph, use failproofai-sdk[langchain]. Supported: langchain-core 1.4.7 to 2.0, langgraph 1.2 to 2.0. Outside that range the adapter still installs and warns once.

Instrument

instrument() registers a tracer through langchain_core.tracers.context.register_configure_hook. LangChain injects it into every callback manager it builds, so graphs, tools, and models are captured without changing a call site — including ones inside libraries you did not write.

What gets recorded

A node becomes a hook, not a nested agent. agent_id is the primary facet across every dashboard surface — promoting retrieve, grade_documents and should_continue to agents would drown it, and label the session after whichever node happened to run first. Hook spans render the same way and still give you a per-node latency view.
Name your nodes whatever you like. A node’s run is identified by its shape — a non-leaf run carrying LangGraph’s own step tag — never by its name.
Naming a node after the thing it runs used to make that thing’s events disappear. It no longer does.

Streaming

.stream() and .astream() emit no per-token events. They fold into the closing model_response:

Token counts on a streamed response

Separate matter, and easy to miss: OpenAI only sends usage on a streamed response when asked.
The adapter records what the framework hands it. Without that flag there is nothing to record, and model_response arrives with no token counts.

Example

Name your spans

By default the root span takes the graph’s own name. Wrap it to get a label you chose:
For multi-agent setups, nest the scopes. Each worker becomes a child span carrying parent_id:
Keep agent_id low cardinality. Use a role or node name, never a UUID or a per-run string.

Control the session

The session id resolves in this order, first match winning:
  1. instrument("langchain", session_id=...)
  2. config={"metadata": {"failproofai_sdk_session_id": ...}}
  3. The enclosing failproofai_sdk.session() scope
  4. metadata["session_id"], metadata["conversation_id"], or metadata["thread_id"]
  5. The root run id
It is never generated from scratch, because a synthesized id splits one run across several sessions.

Options

Set capture_content=False for regulated data. Structure, timings, token counts, tool names, and outcomes are still recorded; message bodies are not. include_chains applies to nested runs only. A runnable you invoke at the top level is the session’s root, so it becomes the agent span rather than a hook pair, and naming it here has no effect.

Human in the loop

interrupt() produces four events, and neither pair is redundant:
human_wait to human_input carries the prompt and the answer (both are dropped under capture_content=False, along with retrieval document sources — the document count survives). agent_pause to agent_resume is the only pair that feeds paused time, so without it a ten-minute human wait is billed as active agent time. The root span stays open across the gap, keeping both calls in one session.

Common problems

create_react_agent propagates the exception. To let the model see the failure and continue, build the tool node explicitly:
The failure is recorded as a tool_result carrying an error either way. This only decides whether the run survives it.
A direct llm.invoke() outside any graph has no parent run, so it opens a root span and emits its model pair inside it. The dashboard parents leaves to an open agent, so the span is deliberate. Name it:
You passed a Failproof handler in config={"callbacks": [...]} as well as calling instrument(). Remove it. The configure hook already covers every callback manager in the process.
They do not. LangGraph raises GraphInterrupt through the same path as a real exception, so every pause reaches the tracer as an error callback. Any GraphBubbleUp subclass is treated as control flow instead, so an approval does not paint a red error.
Check in this order: instrument() ran before the graph executed; there is a with failproofai_sdk.session(): around the call; FAILPROOFAI_SDK_STRICT=1 set, so a degraded hook raises instead of being swallowed.

Next

How it works

Pairs, ids, session lifecycle, and delivery.

Read a trace

Follow causality through the session you just captured.

Other frameworks

CrewAI, LlamaIndex, Pydantic AI, and custom agents.