LangChain's BaseCallbackHandler exposes start/end/error events for LLM calls, chains, tools, and retrievers, plus agent action/finish, and nothing about GPU or auth.
Imagine a relay race coach who can only stopwatch the runners on her own team. She can clock when each runner starts a leg, when they finish, and whether they dropped the baton. She cannot clock the scoreboard wiring or the parking attendants because those are not her runners. LangChain's coach watches its own runners, model calls, chains, tools, retrievers, and tells you when each one started, ended, or tripped. Anything happening outside her team, like graphics-card timings or login screens, lives on a different scoreboard entirely.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
The LangChain callback API looks like a small thing and is actually the integration point for the entire observability ecosystem around the framework. Every tracing product that claims a LangChain integration, LangSmith, Langfuse, Phoenix, Helicone, Weave, the OpenTelemetry auto-instrumentation packages, does its work through this one interface.
Getting the surface right matters for two reasons. First, knowing which events exist lets you wire tracing without writing extra glue. Second, knowing which events do not exist saves you from instrumenting the wrong layer and shipping observability that misses the signal you actually need.
The shape of the surface
BaseCallbackHandler is an abstract base with around twenty methods. They group into five clusters by the kind of operation each one wraps.
LLM calls
A start/end/error trio (on_llm_start, on_llm_end, on_llm_error). These fire around every model invocation that goes through a LangChain BaseLLM or BaseChatModel. The start hook receives the serialized model config and the prompts list; the end hook receives the response with usage metadata if the provider returned it.
Chains
A matching start/end/error trio for chains. A chain in LangChain is any Runnable. RunnableSequence, RunnableParallel, custom RunnableLambda. The chain events fire around the orchestrator, not the individual steps inside it; an LCEL pipeline of three steps produces one chain event triple plus the per-step events for whatever runs inside.
Tools
A start/end/error trio for tools. These fire whenever an agent or chain invokes a BaseTool. The start hook gets the tool name and the input string; the end hook gets the tool's output. This is the primary surface for instrumenting function calls.
Retrievers
A start/end/error trio for retrievers. Fires around BaseRetriever.invoke. The end hook gets the list of returned documents, which makes it the natural place to instrument RAG retrieval quality.
Agents
on_agent_action and on_agent_finish. These exist because the agent loop is a control structure that emits decisions, not just runs. on_agent_action fires when the agent picks a tool to call; on_agent_finish fires when the agent decides to stop. Both are useful for debugging loops that wander.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
from langchain_core.callbacks import BaseCallbackHandler
class MyTracer(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, *, run_id, parent_run_id=None, **kw):
print(f'LLM start: {run_id} parent={parent_run_id}')
def on_llm_end(self, response, *, run_id, **kw):
print(f'LLM end: {run_id} usage={response.llm_output}')
def on_tool_start(self, serialized, input_str, *, run_id, **kw):
print(f'Tool start: {serialized["name"]} run={run_id}')
def on_tool_error(self, error, *, run_id, **kw):
print(f'Tool error: {run_id} err={error}')Real products, models, and research that use this idea.
- Langfuse's LangChain integration registers a BaseCallbackHandler subclass and re-emits each event as an OpenTelemetry span.
- LangSmith's tracing client is a callback handler under the hood; nothing magic happens that you cannot replicate.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow would you implement a redaction layer that strips PII from prompts before they hit a tracing backend?
Subclass BaseCallbackHandler, override on_llm_start to run a redaction pass over the prompts list, then forward the redacted version to the downstream tracer; keep the redaction rules in a shared library so other entry points use the same patterns.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Assuming the callback surface includes provider-side details like token by token GPU timings, then writing custom handlers for events that never fire.
60 second bullets to scan on the way to the call.
The four primary callback categories and their start/end/error shape
The two agent-specific events and what they signal
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.