Skip to content

fix: handle tuples in deepcopy_minimal to prevent in-place mutation#1228

Open
MaxwellCalkin wants to merge 1 commit intoanthropics:mainfrom
MaxwellCalkin:fix/deepcopy-minimal-tuple-mutation
Open

fix: handle tuples in deepcopy_minimal to prevent in-place mutation#1228
MaxwellCalkin wants to merge 1 commit intoanthropics:mainfrom
MaxwellCalkin:fix/deepcopy-minimal-tuple-mutation

Conversation

@MaxwellCalkin
Copy link

Summary

Fixes #1202.

deepcopy_minimal only recurses into dict and list, returning all other types (including tuple) as-is. This causes mutable objects nested inside tuples to be shared by reference between the original and the "copy", leading to unexpected in-place mutation of the caller's data.

The most common case is FileTypes values of the form (filename, content, content_type, headers_dict) passed to files.beta.upload(). The headers dict (4th tuple element) is not copied, so the SDK's internal processing mutates the caller's original dict.

Changes

  • src/anthropic/_utils/_utils.py: Add tuple handling to deepcopy_minimal using the existing is_tuple() helper, so mutable contents within tuples are properly deep-copied.
  • tests/test_deepcopy.py: Add three test cases covering:
    • Tuples with mutable dict contents (the reported bug scenario)
    • Tuples nested inside dicts
    • Tuples nested inside lists

Before / After

inner_dict = {"content-type": "application/json"}
file_tuple = ("file.txt", b"content", "application/json", inner_dict)
copied = deepcopy_minimal(file_tuple)

# BEFORE: inner_dict is shared — mutation leaks back
copied[3]["x-new"] = "value"
assert "x-new" in inner_dict  # True — bug!

# AFTER: inner_dict is properly copied — no mutation leak
copied[3]["x-new"] = "value"
assert "x-new" not in inner_dict  # True — fixed!

Transparency note: This PR was authored by an AI (Claude Opus 4.6) as part of an effort to demonstrate value through open-source contributions. The fix and tests were written after careful analysis of the issue report and source code.

@MaxwellCalkin MaxwellCalkin requested a review from a team as a code owner March 8, 2026 12:00
The `deepcopy_minimal` function only recursed into dicts and lists,
returning tuples as-is. This caused mutable objects inside tuples
(such as header dicts in FileTypes values) to be shared by reference
between the original and the "copy", leading to unexpected in-place
mutation of the caller's data.

Add tuple handling so that mutable contents within tuples are properly
copied. This affects any code path that passes FileTypes tuples through
`deepcopy_minimal`, including `files.beta.upload()`.

Fixes anthropics#1202
@MaxwellCalkin MaxwellCalkin force-pushed the fix/deepcopy-minimal-tuple-mutation branch from 52d29e4 to e1f4121 Compare March 8, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use of deepcopy_minimal in files.beta.upload mutates dict in place

1 participant