langchain-core’s callback manager, so instrumenting one instruments the other.
Install
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.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:parent_id:
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:instrument("langchain", session_id=...)config={"metadata": {"failproofai_sdk_session_id": ...}}- The enclosing
failproofai_sdk.session()scope metadata["session_id"],metadata["conversation_id"], ormetadata["thread_id"]- The root run id
Options
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
A raising tool aborts the whole graph
A raising tool aborts the whole graph
create_react_agent propagates the exception. To let the model see the failure and continue, build the tool node explicitly:tool_result carrying an error either way. This only decides whether the run survives it.An agent named after the model class appears in the trace
An agent named after the model class appears in the trace
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:Every event appears twice
Every event appears twice
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.Human approvals show as errors
Human approvals show as errors
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.Nothing is recorded
Nothing is recorded
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.

