Merging LangSmith Runs from Remote Graph

Last updated: December 18, 2025

Problem

When using LangGraph Server via the RemoteGraph pattern, LangSmith displays two separate runs (rows) per single user message - one for the client-side orchestrator and one for the server-side graph execution. This creates fragmented traces that are difficult to analyze.

Solution

Enable distributed tracing to merge these runs into a single unified trace hierarchy. This requires configuration on both the client and server sides.

Step 1: Enable Distributed Tracing on the Client

Set distributed_tracing=True when creating your RemoteGraph instance:

from langgraph.pregel.remote import RemoteGraph

remote_graph = RemoteGraph(
    "my-graph",
    url="https://my-deployment.langgraph.app",
    distributed_tracing=True  # Enable trace context propagation
)

When enabled, the client automatically adds langsmith-trace headers containing the trace context (dotted_ordertrace_id, etc.) to all requests sent to the server.

Step 2: Configure the Server to Accept Trace Context

On the server side, use a context manager to extract and apply the trace context from incoming requests:

For synchronous graphs:

import contextlib
import langsmith as ls

my_graph = builder.compile(name="ReAct Agent")

@contextlib.contextmanager
def graph(config):
    configurable = config["configurable"]
    parent_trace = configurable.get("langsmith-trace")
    parent_project = configurable.get("langsmith-project")
    with ls.tracing_context(parent=parent_trace, project=parent_project):
        yield my_graph

For asynchronous graphs:

import contextlib
import langsmith as ls

my_graph = builder.compile(name="ReAct Agent")

@contextlib.asynccontextmanager
async def graph(config):
    configurable = config["configurable"]
    parent_trace = configurable.get("langsmith-trace")
    parent_project = configurable.get("langsmith-project")
    with ls.tracing_context(parent=parent_trace, project=parent_project):
        yield my_graph

How It Works

  1. Client side: When distributed_tracing=True, the RemoteGraph calls RunTree.to_headers() which produces:

    • langsmith-trace: Contains the dotted_order for trace correlation

    • baggage: Contains langsmith-projectlangsmith-metadata, and langsmith-tags

  2. Server side: LangGraph Server extracts these headers and passes them through the configurable dict:

    • langsmith-trace: The trace context string

    • langsmith-project: The project name to trace to

    • langsmith-metadata: Any metadata passed from the client

    • langsmith-tags: Any tags passed from the client

  3. Trace merging: The ls.tracing_context(parent=...) context manager reconstructs the parent run tree, causing all server-side spans to appear as children of the client's trace.

Requirements

  • langsmith SDK: Version >= 0.4.56 (earlier versions have issues with malformed dotted_order values)

  • LangGraph SDK: Any recent version with RemoteGraph support

Troubleshooting

Traces Still Appear Separate

  1. Verify distributed_tracing=True is set on the client's RemoteGraph instance

  2. Check SDK versions: Ensure langsmith >= 0.4.56

  3. Confirm server-side context manager: The server must explicitly use tracing_context(parent=...) to opt-in to distributed tracing

Missing Project or Metadata

Ensure you're extracting both langsmith-trace and langsmith-project from the configurable:

parent_trace = configurable.get("langsmith-trace")
parent_project = configurable.get("langsmith-project")

Alternative: Trace to Different Projects

If you prefer to keep the traces separate but organized, you can trace the client and server to different LangSmith projects instead of merging them.

Related Documentation