Skip to main content
LangSmith tells you that a run misbehaved: it errored, scored low on an eval, or a human thumbs-downed it. It can’t tell you whether the person on the other end was actually blocked, confused, or fine. Subtext can. Attach the Subtext session URL to your LangSmith trace metadata and every flagged run becomes a jumping-off point — your agent reads the subtext_url, opens that person’s real session, and walks the decisive moments with screenshots, the component tree, network, and console. LangSmith detects; Subtext diagnoses.
This guide assumes the Subtext capture snippet is installed and the langsmith SDK is installed with tracing enabled (LANGSMITH_TRACING=true, LANGSMITH_API_KEY set), or that you run on LangSmith Deployment (formerly LangGraph Platform).

Attach the session URL

LangSmith is the exception to Subtext’s flat-property convention. Most integrations attach subtext_url as a top-level property on a user, session, or event record. A LangSmith run has no such column — its context lives in metadata, a dict attached at run-create time. Attach subtext_url there, nested under metadata, on the root run (the top of the trace) so anything reading the trace’s metadata sees it without walking every child run. Which surface you use depends on how your agent runs. The URL itself originates in the browser. Capture it with FS('getSession', { format: 'url.now' }) — it returns null until the session has started, so read it after init (and re-read after a re-identify), then send it to your backend with the request that starts the agent run:

@traceable (Python decorator)

A static metadata= on the decorator is fixed at import time, which is no use for a per-end-user URL. Pass it per call instead, via langsmith_extra:

RunTree (manual instrumentation)

config.metadata (LangChain / LangGraph runnables)

The common case: your agent is a LangChain or LangGraph runnable invoked once per end-user request. Thread the session URL through the invoke config — the active LangChainTracer reads config["metadata"] and writes it onto the run automatically, no direct tracer call needed.

LangSmith Deployment (deployed assistants)

If your agent runs as a LangSmith Deployment, attach the same key in the run-create call’s top-level metadata field, whether from the SDK or REST directly. This is the surface a backend or proxy uses when it — not the browser — holds both the end-user’s session identity and the call that starts the agent run.
Don’t expect subtext_url to appear as a filterable top-level column — it’s nested under metadata, so a query needs FQL like eq(metadata_key, "subtext_url"), not a bare property filter. Don’t attach it only on a child run: a person reading the root-run metadata panel, or an agent fetching the trace by id, won’t see it there. And don’t store a raw session id where the URL belongs — attach the clickable viewer URL, since that is what gets opened directly. The value written at run-create is frozen, so if the same end-user drives a multi-turn thread, re-attach the current subtext_url on each new run — in the invoke config["metadata"] for runnables, or the run-create metadata field on LangSmith Deployment.

From signal to session

However you first hear about a bad run in LangSmith — a flagged trace, an eval regression, an error spike — the path is the same: get to the subtext_url in the run’s metadata, then hand it to your agent for a Detect-vs-Diagnose read. If you land on a child run and don’t see it, jump to the root run, where it’s attached.
Scanning the traces table (or an annotation queue) in your LangSmith project, you spot a run someone tagged low-quality, or thumbs-downed from your product’s feedback widget.Open the run, go to the Metadata panel, and copy the subtext_url value. If you’re on a child run and don’t see it, jump to the root run.
An online evaluator’s score (say answer_correctness, or your own review_quality feedback key) drops week-over-week on the LangSmith experiments dashboard.Filter the project’s runs by the low-scoring feedback key, open a handful of the worst-scoring runs, and copy subtext_url off each one’s metadata.
A spike in errored runs on a specific chain or node — LangSmith’s own monitoring, or an Automation-driven Slack/webhook alert, flags it.Filter runs by error for the affected chain in the window, open a few, and copy subtext_url off each.
For a spike or regression across several runs, ask your agent to roll the findings into one proof document (doc-create) with a screenshot per session and a single shared link — then paste the synthesis back onto the run as a LangSmith comment or annotation, so the score or error carries a human explanation, not just a number.

For agents

An autonomous agent can run the whole loop against LangSmith directly: discover the flagged runs, extract the subtext_url from trace metadata, and hand each session to the Subtext MCP. LangChain ships an official MCP server, langsmith-mcp-server — run it locally over stdio (pip install langsmith-mcp-server or uvx langsmith-mcp-server) or in Docker, authenticated with LANGSMITH_API_KEY. Repo: github.com/langchain-ai/langsmith-mcp-server; docs: docs.langchain.com/langsmith/langsmith-mcp-server.
1

Discover flagged runs

With the MCP, fetch_runs is the discovery tool — fetch a trace tree by trace_id, filtering by error, run_type, or a raw FQL (Filter Query Language) query. Without the MCP, Client.list_runs takes the same FQL filter:
See the trace query syntax reference for the full filter grammar.
2

Extract the session URL

Read subtext_url off the flagged run’s root-run metadata. A run fetched from deep in the trace tree may not carry it, so walk up to the root run (no parent_run_id) if the child run you matched doesn’t have it:
3

Hand off to Subtext

For each URL, call review-open(session_url=<subtext_url>) on the Subtext MCP, then step through at the flagged moment for a Detect-vs-Diagnose read. Batch several runs’ URLs to review a spike or regression together for the common cause.
subtext_url only round-trips if it was attached to the root run at trace-create time (see Attach the session URL). It’s metadata, not a top-level column, so discover by error, score, or feedback first, then read subtext_url off the result. Treat a missing value as “not instrumented,” not an error.