withSpan or @span decorators needed on your graph nodes.
Canonical signatures: TypeScript reference · Python reference
Supported Languages
Working with plain LangChain (no LangGraph)? The same handler serves both, and the SDK exposes it under a LangChain name too:
getLangChainCallbackHandler() / get_langchain_callback_handler() and the BitfabLangChainCallbackHandler class are aliases of their LangGraph counterparts. Use whichever name matches your stack; the behavior is identical.
Quick Start
Plain LangChain Chains
The handler works the same way on LangChain chains and runnables; pass it incallbacks when invoking:
What Gets Captured
The callback handler hooks into LangChain’s callback system and creates spans automatically:Trace Lifecycle
The first callback in a framework invocation becomes the trace root. For full LangGraph runs this is usually a chain/graph node; for plain LangChain usage it can also be a direct chat model, LLM, tool, or retriever call. The handler registers that root immediately as a pending external trace, then completes it when the root callback ends, so long-running invocations can appear as in progress before their final output is available. If the handler runs inside an active BitfabwithSpan / @span root, its spans attach to that outer trace instead. In that case the handler still records the framework span tree, but the outer Bitfab root owns final trace completion.
You do not need an outer withSpan / @span root when the workflow is just the LangGraph or LangChain invocation. Add one only when there is meaningful application work around invoke() that should be part of the same trace, such as input preparation, retrieval outside LangChain, post-processing, persistence, or downstream service calls. When you do add an outer root, use the same trace function key for the outer span and the callback handler.
LangGraph Metadata
LangGraph-specific metadata is automatically extracted and stored as span context:langgraph_step: Current step numberlanggraph_node: Current node namelanggraph_triggers: What triggered this nodelanggraph_path: Execution pathlanggraph_checkpoint_ns: Checkpoint namespace
Token Usage
For LLM spans, token usage is captured from the LLM result. The handler prefers LangChain’s standardizedusage_metadata on each generation’s message (which is also how usage arrives on the final aggregated chunk of streaming runs), then falls back to provider-native response_metadata shapes (OpenAI, Anthropic, Google Gemini / Vertex), and finally the legacy llm_output.token_usage location:
inputTokens: Prompt tokens. For Anthropic, cache reads and cache creation are added back so the value reflects the true prompt size.outputTokens: Completion tokenstotalTokens: Total tokenscachedInputTokens: Cached prompt tokens, fromusage_metadata.input_token_details.cache_read, OpenAIprompt_tokens_details.cached_tokens, or Anthropiccache_read_input_tokensmodel: Model name (extracted from serialized config or metadata)
stream_usage: true / stream_options: {"include_usage": true}), the fields are left unset rather than estimated.
TypeScript
Installation
Method Signature
traceFunctionKey(string, required): Groups all traces from this handler under one key in Bitfab
BitfabLangGraphCallbackHandler that implements the LangChain callback handler interface (duck-typed, no @langchain/core dependency required).
Usage
Callback Hooks
LangChain invokes these callback hooks on the handler:Nesting with Core Tracing
The handler integrates with Bitfab’s span stack. If the application has meaningful work around the graph or chain invocation, create a same-keywithSpan wrapper around that outer workflow and the LangGraph spans nest as children. Handler-only instrumentation is enough for a plain LangGraph / LangChain call:
bitfab.getFunction(...) and bitfab.getLangGraphCallbackHandler(...) take a key; pass the same key ("my-pipeline" above) to both. If you use two different keys here, the same flow will register as two separate overlapping trace functions in the dashboard, an anti-pattern to avoid.
Error Handling
- GraphBubbleUp: LangGraph’s internal interrupt mechanism. Detected automatically and completed silently (no error recorded).
- All other errors: Error message captured in the span. The handler never throws; all callbacks are wrapped in try/catch.
- Missing serialized callback data: Some framework versions can pass nullish serialized payloads. The handler still records the span using fallback names such as
chain,llm,tool, orretriever. - Reusability: The handler resets after each root span completes and can be reused across multiple invocations.
Python
Installation
Method Signature
trace_function_key(str, required): Groups all traces from this handler under one key in Bitfab
BitfabLangGraphCallbackHandler instance that extends BaseCallbackHandler from langchain-core.
Usage
Callback Methods
The handler implements these LangChain callback methods:Nesting with Core Tracing
Bind the key once withget_function so the root and the handler share it (no repeated string to keep in sync). Use this only when the application has meaningful work around the graph or chain invocation; handler-only instrumentation is enough for a plain LangGraph / LangChain call:
get_function does this for you. The plain @bitfab.span("my-pipeline") / bitfab.get_langgraph_callback_handler("my-pipeline") forms work too, but you must pass the same key to both, otherwise the same flow registers as two separate overlapping trace functions in the dashboard, an anti-pattern to avoid.
Error Handling
- GraphBubbleUp: LangGraph’s internal interrupt mechanism. Detected automatically and completed silently (no error recorded).
- All other errors:
repr(error)captured in the span. The handler never raises; all callbacks are wrapped in try/except. - Missing serialized callback data: Some framework versions can pass nullish serialized payloads. The handler still records the span using fallback names such as
chain,llm,tool, orretriever. - Reusability: The handler resets after each root span completes and can be reused across multiple invocations.
Replay
Handler-instrumented graphs are fully replayable, even though no@span / withSpan root exists in the application code. The handler records the graph invocation as the root span, with the initial graph state as the recorded input. To replay, pass the handler’s key plus a plain callable that re-invokes the graph (the SDK wraps it internally):
config["configurable"] values, dependency objects, API keys); the trace records only the graph state. Use no-op substitutes for side-effectful wiring (billing callbacks, notification senders). On SDKs that predate explicit-key replay, wrap the callable under the same key yourself (Python: @bitfab.span("weather-agent") with a (**state) signature; TypeScript: getFunction("weather-agent").withSpan(...)). Full details: Replaying handler-instrumented functions in the Python SDK and TypeScript SDK pages.