LangChain/Core Concepts
Intermediate12 min

Callbacks & Event Hooks

LangChain's callback system hooks into every stage of chain execution — but most teams reach for it when LangSmith, astream_events, or @traceable would serve them better. This article teaches you which mechanism to reach for, how to write production-grade handlers, and how callbacks fail in ways that bring down your whole chain.

Quick Reference

  • Start with LangSmith (2 env vars) — it covers 80% of observability needs with zero code
  • Use astream_events v2 when you need real-time event streaming to a UI or SSE endpoint
  • Use @traceable when you want LangSmith tracing on arbitrary functions outside LangChain Runnables
  • Write BaseCallbackHandler only for custom logic LangSmith can't provide: custom metrics push, cost alerts, external integrations
  • A sync callback that throws crashes the entire chain — always isolate with try/except
  • dispatch_custom_event lets you emit structured progress events from inside any Runnable
  • AsyncCallbackHandler is required in async applications — sync handlers block the event loop and add latency to every call

When NOT to Write a Custom Callback

Before writing a callback handler, check whether one of these three alternatives already covers your need — in order of preference:

Need instrumentation?traces, metrics, or progress eventsLangSmith covers it?traces, latency, token countsyesLangSmithenv vars onlynoNeed real-time streaming?progress to UI, SSE, websocketyesastream_eventsv2 — no handler classnoCustom logic on every LLM/tool event?cost tracking, metrics push, alertingyesCallbackBaseCallbackHandlerno@traceablearbitrary functions

start with LangSmith — reach for callbacks only when LangSmith can't do it

NeedReach forWhy
Full traces, latency, token countsLangSmith (env vars)Zero code — traces every Runnable automatically
Real-time events to a UI or SSE streamastream_events v2No handler class needed — async generator over events
Trace arbitrary non-LangChain functions@traceable decoratorWorks on any Python function, not just Runnables
Custom metrics push, cost alerts, side effects on every LLM/tool eventBaseCallbackHandlerWhen you need code to run on every event, every time
Most 'I need a callback' tickets are actually LangSmith tickets

If your goal is to see what happened in a chain — inputs, outputs, latency, token usage, errors — LangSmith gives you all of it with two environment variables. Write a custom handler only when you need to act on events programmatically: push to a metrics system, fire an alert, compute a running cost total.