Skip to content

fix(claude-agent-sdk): keep a caller-supplied session.id over the SDK session_id - #3777

Open
iaj6 wants to merge 1 commit into
Arize-ai:mainfrom
iaj6:fix/claude-agent-sdk-context-session-id
Open

iaj6 wants to merge 1 commit into
Arize-ai:mainfrom
iaj6:fix/claude-agent-sdk-context-session-id

Conversation

@iaj6

@iaj6 iaj6 commented Sep 17, 2026

Copy link
Copy Markdown

Fixes #3775

What changed

A session.id supplied by the caller through OpenInference context (setSession(context.active(), { sessionId })) is applied to the AGENT span at start by OITracer, and was then overwritten by the SDK's own session_id on the system/init message and again on the result message. This happened in three places: processMessage() for query(), processSessionMessage() for V2 session turns, and wrapPrompt() via extractResultSuccessAttributes()/extractResultErrorAttributes().

Now each span checks its captured context once (hasContextSessionId()), and when the caller supplied a session id the SDK write is skipped (withoutSessionId() on the result attributes; the init handler omits it). Without a context session the SDK id still fills session.id exactly as before, so the existing assertion in v1Query.test.ts (session.id === "sess-123") is unchanged.

js/AGENTS.md lists "propagate context attributes via OITracer (session ID, user ID, metadata, tags)" as a must for every instrumentor. This package already propagates the session id; this PR stops it from being clobbered afterwards, which brings it in line with how metadata, user id and tags are already handled (applied at start, never overwritten by message processing).

Why it matters in Phoenix

Phoenix binds a trace to the first session.id it ingests for that trace and ignores later ones. Spans export when they end, so in a turn with tool calls the TOOL spans (which keep the context session id) land first and the trace goes into the caller's session; in a turn with no tool calls the AGENT span lands first with the SDK id and the trace goes into a session named after the SDK UUID. The same agent's turns end up split across two sessions depending on whether it happened to use a tool. Reproduced against the real @anthropic-ai/claude-agent-sdk@0.3.218 with one turn (output in the issue).

Tests

Three new tests, one per code path, each wrapping the call in context.with(setSession(...)) and asserting the AGENT span keeps the context session id (and still records the model name from init):

  • query() with system/init + result messages
  • unstable_v2_prompt()
  • unstable_v2_createSession() turn via send() + stream()

They use provider.register() to install the async-hooks context manager for the duration of the test and reset the globals in finally, so no new devDependency. All three fail on main and pass here; the rest of the suite is unchanged (45/45).

Trade-off to flag

When the caller sets a session id, the SDK's session_id no longer appears on the span at all. It was only ever there by accident in that case, but it is a useful value (it is what claude --resume takes). If you would like it kept under a dedicated attribute when the context wins, say the word and I will add it to this PR; I left it out to keep the change to the reported behaviour.

Changeset included (patch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MXxV7qtX4GyTfo7QWaREnL

… session_id

OITracer applies a session id set through OpenInference context (setSession)
when the AGENT span starts; message processing then overwrote it with the
SDK's own session_id on the system/init message and again on the result, for
query(), unstable_v2_prompt(), and V2 session turns. js/AGENTS.md lists
context propagation of the session id as a must for every instrumentor.

Phoenix binds a trace to the first session.id it ingests, so a turn with no
tool calls (AGENT span ingested first, carrying the SDK id) landed in a
different session from a turn with tool calls (TOOL spans first, carrying the
context id).

Check the captured context once per span; when it supplies a session id,
skip the SDK write. Without a context session the SDK id still fills
session.id as before.

Fixes Arize-ai#3775

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MXxV7qtX4GyTfo7QWaREnL
@iaj6
iaj6 requested a review from a team as a code owner September 17, 2026 17:36
@iaj6

iaj6 commented Sep 17, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[bug] claude-agent-sdk (JS): SDK session_id overwrites a caller-supplied session.id from OpenInference context

1 participant