Skip to content

Python: fix(anthropic): don't mutate caller metadata when mapping user to user_id - #8816

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 1 commit into
microsoft:mainfrom
anishmehta24:fix/anthropic-metadata-mutation
Sep 29, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 1 commit into
microsoft:mainfrom
anishmehta24:fix/anthropic-metadata-mutation

Conversation

@anishmehta24

Copy link
Copy Markdown
Contributor

Motivation & Context

When user is set, AnthropicClient._prepare_options maps it to metadata.user_id, but it writes user_id straight into the metadata dict the caller passed in. If the same metadata dict is reused across requests (a shared default, a per-session trace dict, etc.), the first user sticks: the "user_id" not in metadata check sees the leaked value on the next call and every later request goes out attributed to the first user.

client = AnthropicClient(api_key="x", model="claude-sonnet-4-5")
meta = {"trace": "t1"}
msgs = [Message(role="user", contents=[Content.from_text("hi")])]
client._prepare_options(msgs, {"user": "alice", "metadata": meta})["metadata"]
# {'trace': 't1', 'user_id': 'alice'}
meta
# {'trace': 't1', 'user_id': 'alice'}   <- caller's dict was changed
client._prepare_options(msgs, {"user": "bob", "metadata": meta})["metadata"]
# {'trace': 't1', 'user_id': 'alice'}   <- bob's request is sent as alice

merge_chat_options only shallow-copies the top-level options, so the nested metadata dict passed to get_response is the same object that reaches _prepare_options.

Description & Review Guide

  • What are the major changes? The user -> metadata.user_id mapping now works on a copy of the metadata dict instead of the caller's one.
  • What is the impact of these changes? The caller's metadata is left untouched, and each request gets the user_id for its own user. An explicit user_id already in metadata still wins, as before.
  • What do you want reviewers to focus on? Nothing in particular, it's a one-line change.

Related Issue

No existing issue.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Test: test_prepare_options_user_does_not_mutate_caller_metadata in packages/anthropic/tests/test_anthropic_client.py; it fails on main. The anthropic tests pass (196 passed, 9 skipped), ruff check/format are clean, and pyright shows no new errors.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Sep 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anish Mehta (@anishmehta24) Reviewed the current head and found no blocking issues.

Merged via the queue into microsoft:main with commit b000c15 Sep 29, 2026
48 of 49 checks passed

This branch was successfully deployed

1 active deployment
github-app-auth — bb6680be Deployed Sep 29, 2026 by anishmehta24 via team_check #5454
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants