SIGN IN SIGN UP

fix(langgraph): keep context values out of tracer-derived metadata (#2690)

Values passed via `context` were reaching LangSmith as `extra.metadata`.
On Platform the Agent Server mirrors `context` into `configurable`
(`run.py`, *"keep config and context in sync for user provided
params"*), and `_getTracingMetadataDefaults` promoted every
`configurable` entry whose value passes the primitive-type check in
`_excludeAsMetadata` — so data callers deliberately routed through the
non-persisted, non-traced channel was traced anyway, on the root run and
on `__start__` and every node run.

## Changes

- `_getTracingMetadataDefaults` now recognizes context aliases: a
`configurable` entry that is an own property of `config.context` with an
`Object.is`-equal value is treated as context-provenance, not user
metadata.
- Aliased keys emit `metadata[key] = undefined` rather than being
omitted. `LangChainTracer` merges tracing metadata first-wins, so the
root marker also stops `@langchain/core`'s own configurable promotion
from re-adding the key on child task and node runs — the path that
actually leaked. `undefined` drops out on serialization.
- Widened the helper's parameter to `LangGraphRunnableConfig` so
`config.context` is visible; internal `__`-prefixed keys are never
marked.
- Configurable-only values, values that differ from their context
counterpart, explicit `metadata`, and the platform identity keys
(`thread_id`, `run_id`, `assistant_id`, `graph_id`) are all unaffected.

## Testing

Replaced the tracing tests' `FakeTracer` with a `LangChainTracer` backed
by a stubbed client that captures real `createRun` payloads.
`tracerInheritableMetadata` is only applied to `LangChainTracer`
instances, so a `BaseTracer` subclass cannot observe this channel at all
— the assertions now run against the same metadata LangSmith would
receive, across all three runs (`LangGraph`, `__start__`, node).

## Follow-up required — this half is inert alone

Verified end-to-end against the public
`langchain/langgraphjs-api:0.12.2-node24` image with this build
installed in the deployed graph:

| Configuration | context delivered | tracer metadata leak |
|---|---|---|
| public image, this fix | no — sidecar strips `context` | n/a,
suppression cannot fire |
| public image + sidecar forwarding `context`, this fix | yes | absent |
| public image + sidecar forwarding `context`, published `1.3.7` | yes |
present on `__start__` and node runs |

The sidecar's `StreamEventsPayload` (`langgraph_api/js/client.mts`) has
no `context` field, so zod strips it and `config.context` is `undefined`
inside the graph. A matching `langgraph-api` change is needed for this
fix to take effect in production; it will be opened separately.

Separately out of scope: the Python `run.py` backfill still mirrors
`context` into `configurable`, and that mirrored copy is persisted in
thread state and checkpoint metadata. Different channel, pre-existing by
design.
S
Saad Farooq committed
00f68a1b002d820b95129bcdaf387d2678ead6f0
Parent: 841466e
Committed by GitHub <noreply@github.com> on 8/14/2026, 6:28:36 PM